cache.go 6.4 KB

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