mapper.go 5.5 KB

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