-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclass_loader.go
More file actions
145 lines (123 loc) · 3.89 KB
/
class_loader.go
File metadata and controls
145 lines (123 loc) · 3.89 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
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
// GNU GPL v3 License
// Copyright (c) 2017 github.com/ymhhh
// Package classloader provides a reflection-based class loader inspired by Java/Akka.
// Types are registered by name or by Go package path, and can be looked up concurrently.
//
// Version: v1.0.1
package classloader
import (
"fmt"
"reflect"
"sync"
)
// ClassLoader registers and resolves reflect.Type values, optionally delegating to a parent loader.
type ClassLoader interface {
// GetParent returns the parent loader, or nil for the root loader.
GetParent() ClassLoader
// LoadClass registers the element type of v. Pass a typed nil pointer such as (*MyType)(nil).
// When name is empty, the key defaults to "PkgPath.TypeName".
LoadClass(name string, v interface{}) error
// FindClass looks up a type by custom name (string) or by typed nil / composite value.
FindClass(name interface{}) (reflect.Type, bool)
// FindLoadedClass reports whether name is registered locally or in an ancestor loader.
FindLoadedClass(name string) bool
}
// Default is the process-wide root class loader.
var Default = NewClassLoader(nil)
type defaultClassLoader struct {
parent ClassLoader
// nameTypes maps user-defined names (e.g. "app:Actor") to element types.
nameTypes map[string]reflect.Type
// pathTypes maps "PkgPath.TypeName" keys to element types for type-based lookup.
pathTypes map[string]reflect.Type
locker sync.RWMutex
}
// NewClassLoader creates a loader that falls back to parent when a type is not found locally.
func NewClassLoader(parent ClassLoader) ClassLoader {
return &defaultClassLoader{
parent: parent,
nameTypes: make(map[string]reflect.Type),
pathTypes: make(map[string]reflect.Type),
}
}
func (p *defaultClassLoader) FindLoadedClass(name string) (exist bool) {
_, exist = p.FindClass(name)
return
}
func (p *defaultClassLoader) GetParent() (loader ClassLoader) {
p.locker.RLock()
loader = p.parent
p.locker.RUnlock()
return
}
func (p *defaultClassLoader) LoadClass(name string, v interface{}) error {
if v == nil {
return ErrNilClassValue
}
vType := reflect.TypeOf(v)
if vType == nil {
// Untyped nil has no reflect.Type and cannot identify a class.
return ErrNilClassValue
}
switch vType.Kind() {
case reflect.Ptr, reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
default:
return ErrInvalidClassType
}
elemType := vType.Elem()
pathKey := genKey(elemType)
if name == "" {
name = pathKey
}
// Hold the write lock for the entire check-then-act sequence so concurrent
// readers never observe a partially updated registry.
p.locker.Lock()
defer p.locker.Unlock()
if _, exist := p.findClassLocal(name); exist {
return nil
}
// Skip local registration when an ancestor already owns this name.
if p.parent != nil {
if _, exist := p.parent.FindClass(name); exist {
return nil
}
}
if _, exist := p.findClassLocal(v); !exist {
p.pathTypes[pathKey] = elemType
}
if _, exist := p.findClassLocal(name); !exist {
p.nameTypes[name] = elemType
}
return nil
}
func (p *defaultClassLoader) FindClass(name interface{}) (typ reflect.Type, exist bool) {
p.locker.RLock()
typ, exist = p.findClassLocal(name)
p.locker.RUnlock()
if exist {
return
}
if p.parent != nil {
return p.parent.FindClass(name)
}
return
}
// findClassLocal reads the local maps. The caller must hold at least a read lock,
// or the write lock when called from LoadClass.
func (p *defaultClassLoader) findClassLocal(name interface{}) (typ reflect.Type, exist bool) {
vType := reflect.TypeOf(name)
if vType == nil {
return nil, false
}
switch vType.Kind() {
case reflect.String:
typ, exist = p.nameTypes[name.(string)]
case reflect.Ptr, reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
typ, exist = p.pathTypes[genKey(vType.Elem())]
}
return
}
// genKey builds the canonical type key used in pathTypes, e.g. "main.MyStruct".
func genKey(v reflect.Type) string {
return fmt.Sprintf("%s.%s", v.PkgPath(), v.Name())
}