cache.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // Copyright 2012 The Gorilla Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package schema
  5. import (
  6. "errors"
  7. "reflect"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. )
  12. var invalidPath = errors.New("schema: invalid path")
  13. // newCache returns a new cache.
  14. func newCache() *cache {
  15. c := cache{
  16. m: make(map[reflect.Type]*structInfo),
  17. conv: make(map[reflect.Kind]Converter),
  18. regconv: make(map[reflect.Type]Converter),
  19. tag: "schema",
  20. }
  21. for k, v := range converters {
  22. c.conv[k] = v
  23. }
  24. return &c
  25. }
  26. // cache caches meta-data about a struct.
  27. type cache struct {
  28. l sync.RWMutex
  29. m map[reflect.Type]*structInfo
  30. conv map[reflect.Kind]Converter
  31. regconv map[reflect.Type]Converter
  32. tag string
  33. }
  34. // parsePath parses a path in dotted notation verifying that it is a valid
  35. // path to a struct field.
  36. //
  37. // It returns "path parts" which contain indices to fields to be used by
  38. // reflect.Value.FieldByString(). Multiple parts are required for slices of
  39. // structs.
  40. func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) {
  41. var struc *structInfo
  42. var field *fieldInfo
  43. var index64 int64
  44. var err error
  45. parts := make([]pathPart, 0)
  46. path := make([]string, 0)
  47. keys := strings.Split(p, ".")
  48. for i := 0; i < len(keys); i++ {
  49. if t.Kind() != reflect.Struct {
  50. return nil, invalidPath
  51. }
  52. if struc = c.get(t); struc == nil {
  53. return nil, invalidPath
  54. }
  55. if field = struc.get(keys[i]); field == nil {
  56. return nil, invalidPath
  57. }
  58. // Valid field. Append index.
  59. path = append(path, field.name)
  60. if field.ss {
  61. // Parse a special case: slices of structs.
  62. // i+1 must be the slice index.
  63. //
  64. // Now that struct can implements TextUnmarshaler interface,
  65. // we don't need to force the struct's fields to appear in the path.
  66. // So checking i+2 is not necessary anymore.
  67. i++
  68. if i+1 > len(keys) {
  69. return nil, invalidPath
  70. }
  71. if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil {
  72. return nil, invalidPath
  73. }
  74. parts = append(parts, pathPart{
  75. path: path,
  76. field: field,
  77. index: int(index64),
  78. })
  79. path = make([]string, 0)
  80. // Get the next struct type, dropping ptrs.
  81. if field.typ.Kind() == reflect.Ptr {
  82. t = field.typ.Elem()
  83. } else {
  84. t = field.typ
  85. }
  86. if t.Kind() == reflect.Slice {
  87. t = t.Elem()
  88. if t.Kind() == reflect.Ptr {
  89. t = t.Elem()
  90. }
  91. }
  92. } else if field.typ.Kind() == reflect.Ptr {
  93. t = field.typ.Elem()
  94. } else {
  95. t = field.typ
  96. }
  97. }
  98. // Add the remaining.
  99. parts = append(parts, pathPart{
  100. path: path,
  101. field: field,
  102. index: -1,
  103. })
  104. return parts, nil
  105. }
  106. // get returns a cached structInfo, creating it if necessary.
  107. func (c *cache) get(t reflect.Type) *structInfo {
  108. c.l.RLock()
  109. info := c.m[t]
  110. c.l.RUnlock()
  111. if info == nil {
  112. info = c.create(t, nil)
  113. c.l.Lock()
  114. c.m[t] = info
  115. c.l.Unlock()
  116. }
  117. return info
  118. }
  119. // create creates a structInfo with meta-data about a struct.
  120. func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
  121. if info == nil {
  122. info = &structInfo{fields: []*fieldInfo{}}
  123. }
  124. for i := 0; i < t.NumField(); i++ {
  125. field := t.Field(i)
  126. if field.Anonymous {
  127. ft := field.Type
  128. if ft.Kind() == reflect.Ptr {
  129. ft = ft.Elem()
  130. }
  131. if ft.Kind() == reflect.Struct {
  132. c.create(ft, info)
  133. }
  134. }
  135. c.createField(field, info)
  136. }
  137. return info
  138. }
  139. // createField creates a fieldInfo for the given field.
  140. func (c *cache) createField(field reflect.StructField, info *structInfo) {
  141. alias, options := fieldAlias(field, c.tag)
  142. if alias == "-" {
  143. // Ignore this field.
  144. return
  145. }
  146. // Check if the type is supported and don't cache it if not.
  147. // First let's get the basic type.
  148. isSlice, isStruct := false, false
  149. ft := field.Type
  150. if ft.Kind() == reflect.Ptr {
  151. ft = ft.Elem()
  152. }
  153. if isSlice = ft.Kind() == reflect.Slice; isSlice {
  154. ft = ft.Elem()
  155. if ft.Kind() == reflect.Ptr {
  156. ft = ft.Elem()
  157. }
  158. }
  159. if ft.Kind() == reflect.Array {
  160. ft = ft.Elem()
  161. if ft.Kind() == reflect.Ptr {
  162. ft = ft.Elem()
  163. }
  164. }
  165. if isStruct = ft.Kind() == reflect.Struct; !isStruct {
  166. if conv := c.conv[ft.Kind()]; conv == nil {
  167. // Type is not supported.
  168. return
  169. }
  170. }
  171. info.fields = append(info.fields, &fieldInfo{
  172. typ: field.Type,
  173. name: field.Name,
  174. ss: isSlice && isStruct,
  175. alias: alias,
  176. required: options.Contains("required"),
  177. })
  178. }
  179. // converter returns the converter for a type.
  180. func (c *cache) converter(t reflect.Type) Converter {
  181. conv := c.regconv[t]
  182. if conv == nil {
  183. conv = c.conv[t.Kind()]
  184. }
  185. return conv
  186. }
  187. // ----------------------------------------------------------------------------
  188. type structInfo struct {
  189. fields []*fieldInfo
  190. }
  191. func (i *structInfo) get(alias string) *fieldInfo {
  192. for _, field := range i.fields {
  193. if strings.EqualFold(field.alias, alias) {
  194. return field
  195. }
  196. }
  197. return nil
  198. }
  199. type fieldInfo struct {
  200. typ reflect.Type
  201. name string // field name in the struct.
  202. ss bool // true if this is a slice of structs.
  203. alias string
  204. required bool // tag option
  205. }
  206. type pathPart struct {
  207. field *fieldInfo
  208. path []string // path to the field: walks structs using field names.
  209. index int // struct index in slices of structs.
  210. }
  211. // ----------------------------------------------------------------------------
  212. // fieldAlias parses a field tag to get a field alias.
  213. func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
  214. if tag := field.Tag.Get(tagName); tag != "" {
  215. alias, options = parseTag(tag)
  216. }
  217. if alias == "" {
  218. alias = field.Name
  219. }
  220. return alias, options
  221. }
  222. // tagOptions is the string following a comma in a struct field's tag, or
  223. // the empty string. It does not include the leading comma.
  224. type tagOptions []string
  225. // parseTag splits a struct field's url tag into its name and comma-separated
  226. // options.
  227. func parseTag(tag string) (string, tagOptions) {
  228. s := strings.Split(tag, ",")
  229. return s[0], s[1:]
  230. }
  231. // Contains checks whether the tagOptions contains the specified option.
  232. func (o tagOptions) Contains(option string) bool {
  233. for _, s := range o {
  234. if s == option {
  235. return true
  236. }
  237. }
  238. return false
  239. }