-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreflective_dynamic_access.go
More file actions
120 lines (99 loc) · 3.38 KB
/
Copy pathreflective_dynamic_access.go
File metadata and controls
120 lines (99 loc) · 3.38 KB
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
// GNU GPL v3 License
// Copyright (c) 2017 github.com/ymhhh
package classloader
import (
"reflect"
)
// ReflectiveDynamicAccess creates instances by calling a type's Construct method via reflection.
//
// Construct conventions (on the pointer receiver):
// - Method name must be "Construct".
// - Returns nothing, or a single error return value.
// - Argument types must match the values passed to CreateInstanceByName/ByType.
type ReflectiveDynamicAccess struct {
classLoader ClassLoader
}
// NewReflectiveDynamicAccess returns a DynamicAccess backed by classLoader.
// When classLoader is nil, Default is used.
func NewReflectiveDynamicAccess(classLoader ClassLoader) DynamicAccess {
da := &ReflectiveDynamicAccess{
classLoader: Default,
}
if classLoader != nil {
da.classLoader = classLoader
}
return da
}
// CreateInstanceByType Convenience method which given a Class[_] object and a constructor description
// will create a new instance of that class.
func (p *ReflectiveDynamicAccess) CreateInstanceByType(typ reflect.Type, args ...interface{}) (
ins interface{}, err error) {
value := reflect.New(typ)
if !value.IsValid() {
return nil, ErrFailedCreateInstance
}
if err = p.constructInstance(value, args...); err != nil {
return
}
ins = value.Interface()
return
}
// CreateInstanceByName Obtain an object conforming to the type T, which is expected to be instantiated from a class designated by the fully-qualified class name given, where the constructor is selected and invoked according to the args argument.
func (p *ReflectiveDynamicAccess) CreateInstanceByName(name string, args ...interface{}) (
ins interface{}, err error) {
typ, exist := p.classLoader.FindClass(name)
if !exist {
return nil, NotFoundInClassLoader(name)
}
return p.CreateInstanceByType(typ, args...)
}
// constructInstance invokes Construct on a newly allocated pointer value.
func (p *ReflectiveDynamicAccess) constructInstance(val reflect.Value, args ...interface{}) (err error) {
value := val.MethodByName("Construct")
if !value.IsValid() {
return ErrNotFoundConstructMethod
}
methodType := value.Type()
if numOut := methodType.NumOut(); numOut > 1 ||
numOut == 1 && methodType.Out(0) != ErrorType {
return ErrBadActorInitFuncOutNumber
}
if err = validateConstructArgs(methodType, args...); err != nil {
return err
}
valArgs := make([]reflect.Value, len(args))
for i, arg := range args {
valArgs[i] = reflect.ValueOf(arg)
}
fnValues := value.Call(valArgs)
if len(fnValues) > 0 &&
fnValues[0].IsValid() &&
!fnValues[0].IsNil() {
err = fnValues[0].Interface().(error)
}
return
}
// validateConstructArgs checks arity and assignability before reflect.Call to avoid panics.
func validateConstructArgs(methodType reflect.Type, args ...interface{}) error {
if methodType.NumIn() != len(args) {
return ErrBadConstructArgCount
}
for i, arg := range args {
expected := methodType.In(i)
got := reflect.TypeOf(arg)
if got == nil {
if expected.Kind() != reflect.Interface && expected.Kind() != reflect.Ptr {
return ErrBadConstructArgType
}
continue
}
if !got.AssignableTo(expected) {
return ErrBadConstructArgType
}
}
return nil
}
// GetClassFor Obtain a Class[_] object loaded with the right class loader (i.e. the one returned by classLoader).
func (p *ReflectiveDynamicAccess) GetClassFor(name string) (reflect.Type, bool) {
return p.classLoader.FindClass(name)
}