-
Notifications
You must be signed in to change notification settings - Fork 3
/
leaf.go
156 lines (130 loc) · 4.9 KB
/
leaf.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
package autumn
import (
"reflect"
)
// leaf describes a single injected class
type leaf struct {
structureType reflect.Type
structureValue reflect.Value
structureElement reflect.Value
name string
postConstruct reflect.Value
preDestroy reflect.Value
unresolvedDependencies map[string]reflect.Value
resolvedDependencies map[string]reflect.Value
}
// newLeaf constructs a new leaf, using the structure name as the name
func newLeaf(config *config, structurePointer interface{}) *leaf {
leaf := &leaf{
structureType: getStructureType(structurePointer),
structureValue: getStructureValue(structurePointer),
structureElement: getStructureElement(structurePointer),
}
leaf.initializeName(config.leafNameMethod)
leaf.initializeDependencies(config.tagName)
leaf.initializePostConstruct(config.postConstructMethod)
leaf.initializePreDestroy(config.preDestroyMethod)
return leaf
}
// newNamedLeaf constructs a new leaf with the specified name
func newNamedLeaf(config *config, name string, structurePointer interface{}) *leaf {
leaf := &leaf{
structureType: getStructureType(structurePointer),
structureValue: getStructureValue(structurePointer),
structureElement: getStructureElement(structurePointer),
name: name,
}
leaf.initializeDependencies(config.tagName)
leaf.initializePostConstruct(config.postConstructMethod)
leaf.initializePreDestroy(config.preDestroyMethod)
return leaf
}
// initializeName initializes the name for the leaf
func (l *leaf) initializeName(getNameMethod string) {
method := l.structureValue.MethodByName(getNameMethod)
if !method.IsValid() {
l.name = l.structureType.String()
return
}
if method.Type().NumIn() != 0 {
panic(l.structureType.String() + " - " + getNameMethod + " must not take any parameters")
} else if method.Type().NumOut() != 1 {
panic(l.structureType.String() + " - " + getNameMethod + " must return exactly one parameter")
} else if method.Type().Out(0).Kind() != reflect.String {
panic(l.structureType.String() + " - " + getNameMethod + " must return a string")
}
l.name = method.Call([]reflect.Value{})[0].String()
}
// initializeDependencies reads in structure tags to find dependencies
func (l *leaf) initializeDependencies(tagName string) {
l.unresolvedDependencies = map[string]reflect.Value{}
l.resolvedDependencies = map[string]reflect.Value{}
for i := 0; i < l.structureType.NumField(); i++ {
field := l.structureType.Field(i)
dep := field.Tag.Get(tagName)
if len(dep) != 0 {
l.unresolvedDependencies[dep] = l.structureElement.FieldByName(field.Name)
}
}
}
// initializePostConstruct initializes the post construct function for the leaf, panicking if it's invalid
func (l *leaf) initializePostConstruct(postConstructMethod string) {
l.postConstruct = l.structureValue.MethodByName(postConstructMethod)
if !l.postConstruct.IsValid() {
return
}
if l.postConstruct.Type().NumIn() != 0 {
panic(l.structureType.String() + " - " + postConstructMethod + " must not take any parameters")
} else if l.postConstruct.Type().NumOut() != 0 {
panic(l.structureType.String() + " - " + postConstructMethod + " must not return any parameters")
}
}
// initializePreDestroy initializes the pre destroy function for the leaf, panicking if it's invalid
func (l *leaf) initializePreDestroy(preDestroyMethod string) {
l.preDestroy = l.structureValue.MethodByName(preDestroyMethod)
if !l.preDestroy.IsValid() {
return
}
if l.preDestroy.Type().NumIn() != 0 {
panic(l.structureType.String() + " - " + preDestroyMethod + " must not take any parameters")
} else if l.preDestroy.Type().NumOut() != 0 {
panic(l.structureType.String() + " - " + preDestroyMethod + " must not return any parameters")
}
}
// resolveDependencies resolves dependencies for the leaf using the supplied tree
func (l *leaf) resolveDependencies(tree *Tree) {
for name := range l.unresolvedDependencies {
dep := tree.GetLeaf(name)
if dep != nil {
l.setDependency(tree, name, dep)
}
}
}
// setDependency sets a dependency in the leaf
func (l *leaf) setDependency(tree *Tree, name string, leaf *leaf) {
if !l.unresolvedDependencies[name].IsValid() {
panic("Can't set dependency " + name + "in leaf" + l.name)
}
// Set the dependency and move it to "resolved"
l.unresolvedDependencies[name].Set(leaf.structureValue)
l.resolvedDependencies[name] = l.unresolvedDependencies[name]
delete(l.unresolvedDependencies, name)
}
// dependenciesResolved determines if dependencies have been resolved
func (l *leaf) dependenciesResolved() bool {
return len(l.unresolvedDependencies) == 0
}
// callPostConstruct calls the leaf's PostConstruct method if it has one
func (l *leaf) callPostConstruct() {
if !l.postConstruct.IsValid() {
return
}
l.postConstruct.Call([]reflect.Value{})
}
// callPreDestroy calls the leaf's PreDestroy method if it has one
func (l *leaf) callPreDestroy() {
if !l.preDestroy.IsValid() {
return
}
l.preDestroy.Call([]reflect.Value{})
}