You will only see the public (readable) repos here until you login.

mapper.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package orm
  2. import (
  3. "fmt"
  4. "reflect"
  5. "sync"
  6. )
  7. // Prefix used for tagging
  8. var Prefix = "db"
  9. type mapper struct {
  10. cond *sync.Cond
  11. wg *sync.WaitGroup
  12. tbls tables
  13. cbs *mapperCallbacks
  14. init bool
  15. }
  16. // Global mapper context
  17. var ctx *mapper = &mapper{
  18. cond: sync.NewCond(&sync.Mutex{}),
  19. wg: &sync.WaitGroup{},
  20. tbls: make(tables),
  21. cbs: &mapperCallbacks{
  22. ch: make(chan mapperCallback),
  23. list: make([]mapperCallback, 0),
  24. cond: sync.NewCond(&sync.Mutex{}),
  25. },
  26. init: false,
  27. }
  28. func Mapper() *mapper {
  29. return ctx
  30. }
  31. // Map a table (struct) that implements MappableInterface
  32. // Note! Should be mapped in init-function from the that struct
  33. func Map(tbl MappableInterface) *mapper {
  34. fmt.Println("Begin <mapping>")
  35. // Add to working group
  36. ctx.wg.Add(1)
  37. ctx.cond.L.Lock()
  38. // Kick off some routines on first call
  39. if !ctx.init {
  40. ctx.init = true
  41. // Go routine to receive mapping-calls between mapping routines
  42. go func() {
  43. for {
  44. // Receive callback from tables during mapping
  45. select {
  46. case q := <-ctx.cbs.ch:
  47. // Add to callback queue until table is mapped
  48. ctx.wg.Add(1)
  49. ctx.cbs.lock()
  50. ctx.cbs.add(q)
  51. ctx.cbs.unlock()
  52. ctx.cbs.cond.Broadcast()
  53. }
  54. }
  55. }()
  56. // Routine to communicate between other mapping routines
  57. go func() {
  58. for {
  59. // Loop on condition as long as empty
  60. ctx.cbs.lock()
  61. for ctx.cbs.length() == 0 {
  62. ctx.cbs.cond.Wait()
  63. }
  64. // Loop through all new callbacks
  65. for i, l := 0, ctx.cbs.length(); i < l; i++ {
  66. cb := ctx.cbs.get(l - i - 1)
  67. // Ensure callback is ran when columns are mapped
  68. if t := ctx.getTblByName(cb.to); t != nil && t.isMapped() {
  69. // Remove callback from slice
  70. ctx.cbs.remove(l - i - 1)
  71. // Kick off the callback; and lock table
  72. go func(t *table) {
  73. t.lock()
  74. defer t.unlock()
  75. defer ctx.wg.Done()
  76. cb.fn(t)
  77. }(t)
  78. }
  79. }
  80. ctx.cbs.unlock()
  81. }
  82. }()
  83. }
  84. ctx.cond.L.Unlock()
  85. // Start mapping of table
  86. go func() {
  87. t := ctx.addTbl(tbl)
  88. // Mapping should only occur once
  89. if !t.isMapped() {
  90. ctx.mapTbl(t)
  91. ctx.cbs.cond.Broadcast()
  92. }
  93. // Unclock write to allow reading
  94. t.unlock()
  95. ctx.wg.Done()
  96. }()
  97. return ctx
  98. }
  99. // WaitInit should be called in main() to wait for the mapping
  100. // to complete before start using the tables
  101. func WaitInit() {
  102. ctx.wg.Wait()
  103. // Debug print
  104. fmt.Println("Done <mapping>")
  105. //fmt.Println(ctx.tbls)
  106. }
  107. // hasTable checks for table
  108. func (m *mapper) hasTbl(n string) bool {
  109. m.cond.L.Lock()
  110. defer m.cond.L.Unlock()
  111. _, ok := m.tbls[n]
  112. return ok
  113. }
  114. // getTbl should only be called controller; Must lock after
  115. func (m *mapper) getTblByName(n string) *table {
  116. m.cond.L.Lock()
  117. defer m.cond.L.Unlock()
  118. if t, ok := m.tbls[n]; ok {
  119. return t
  120. }
  121. return nil
  122. }
  123. func (m *mapper) getTbl(t MappableInterface) *table {
  124. m.cond.L.Lock()
  125. defer m.cond.L.Unlock()
  126. for tbl, ok := m.tbls[reflect.TypeOf(t).Elem().Name()]; ; {
  127. if tbl == nil || !ok || !tbl.isMapped() {
  128. m.cond.Wait()
  129. }
  130. return tbl
  131. }
  132. }
  133. // addTbl creates a new or returns an existing table; will write lock!
  134. func (m *mapper) addTbl(t MappableInterface) *table {
  135. m.cond.L.Lock()
  136. defer m.cond.L.Unlock()
  137. rt := reflect.TypeOf(t).Elem()
  138. if t, ok := m.tbls[rt.Name()]; ok {
  139. return t.lock()
  140. }
  141. m.tbls[rt.Name()] = &table{
  142. rt: rt,
  143. rv: reflect.ValueOf(t),
  144. l: &sync.RWMutex{},
  145. tFn: t.GetTableMapperFn(),
  146. cFn: t.GetColumnMapperFn(),
  147. cols: make(columns, 0),
  148. rels: relations{
  149. m: &sync.Mutex{},
  150. rmap: make(map[relType][]relation),
  151. },
  152. mapped: false,
  153. }
  154. return m.tbls[rt.Name()].lock()
  155. }
  156. // isPossibleRelation determine if fields in table implements MappableInterface
  157. // then its possible a relation
  158. func (m *mapper) isPossibleRelation(t reflect.Type) bool {
  159. return reflect.New(t).Type().Implements(
  160. reflect.TypeOf((*MappableInterface)(nil)).Elem(),
  161. )
  162. }
  163. // mapField will prepare any field in table struct for mapping
  164. func (m *mapper) mapField(t *table, csf reflect.StructField, cv reflect.Value) field {
  165. switch csf.Type.Kind() {
  166. case reflect.Ptr:
  167. fallthrough
  168. case reflect.Slice:
  169. fallthrough
  170. case reflect.Map:
  171. if x := csf.Type.Elem(); x.Kind() == reflect.Ptr {
  172. if m.isPossibleRelation(x.Elem()) {
  173. return field{
  174. sf: csf,
  175. t: x.Elem(),
  176. v: cv,
  177. }
  178. }
  179. }
  180. if m.isPossibleRelation(csf.Type.Elem()) {
  181. return field{
  182. sf: csf,
  183. t: csf.Type.Elem(),
  184. v: cv,
  185. }
  186. }
  187. case reflect.Struct:
  188. if m.isPossibleRelation(csf.Type) {
  189. return field{
  190. sf: csf,
  191. t: csf.Type,
  192. v: cv,
  193. }
  194. }
  195. }
  196. return field{
  197. sf: csf,
  198. t: csf.Type,
  199. v: cv,
  200. }
  201. }
  202. // mapTbl will simply loop throug every field in the table
  203. func (m *mapper) mapTbl(t *table) {
  204. for n := 0; n < t.getType().NumField(); n++ {
  205. if sf := t.getType().Field(n); !sf.Anonymous {
  206. t.addField(m.mapField(t, sf, t.getValue().Field(n)), m.cbs.ch)
  207. } else if t.getType().Field(n).Anonymous && t.getValue().Field(n).CanInterface() {
  208. cv := t.getValue().Field(n)
  209. csf := reflect.TypeOf(cv.Interface())
  210. for i := 0; i < csf.NumField(); i++ {
  211. if !csf.Field(i).Anonymous {
  212. t.addField(m.mapField(t, csf.Field(i), cv.Field(i)), m.cbs.ch)
  213. }
  214. }
  215. }
  216. }
  217. t.mapped = true
  218. }
  219. // mappcerCallbackFn is used to communicate between mapping routines
  220. type mapperCallbackFn func(tbl *table)
  221. // mappcerCallback is used to communicate between mapping routines
  222. type mapperCallback struct {
  223. from, to string
  224. fn mapperCallbackFn
  225. }
  226. // mappcerCallbacks holds the main channel from main thread
  227. // and a queue for all callbacks
  228. type mapperCallbacks struct {
  229. ch chan mapperCallback
  230. list []mapperCallback
  231. cond *sync.Cond
  232. }
  233. /**
  234. * Below is just helper methods to make code cleaner
  235. */
  236. func (m *mapperCallbacks) lock() *mapperCallbacks {
  237. m.cond.L.Lock()
  238. return m
  239. }
  240. func (m *mapperCallbacks) unlock() {
  241. m.cond.L.Unlock()
  242. }
  243. func (m *mapperCallbacks) add(mf mapperCallback) {
  244. m.list = append(m.list, mf)
  245. }
  246. func (m *mapperCallbacks) get(i int) mapperCallback {
  247. return m.list[i]
  248. }
  249. func (m *mapperCallbacks) remove(i int) {
  250. m.list = append(m.list[:i], m.list[i+1:]...)
  251. }
  252. func (m *mapperCallbacks) length() int {
  253. return len(m.list)
  254. }