cache.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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.isSliceOfStructs && (!field.unmarshalerInfo.IsValid || (field.unmarshalerInfo.IsValid && field.unmarshalerInfo.IsSliceElement)) {
  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.isRequired = 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. m := isTextUnmarshaler(reflect.Zero(ft))
  155. if ft.Kind() == reflect.Ptr {
  156. ft = ft.Elem()
  157. }
  158. if isSlice = ft.Kind() == reflect.Slice; isSlice {
  159. ft = ft.Elem()
  160. if ft.Kind() == reflect.Ptr {
  161. ft = ft.Elem()
  162. }
  163. }
  164. if ft.Kind() == reflect.Array {
  165. ft = ft.Elem()
  166. if ft.Kind() == reflect.Ptr {
  167. ft = ft.Elem()
  168. }
  169. }
  170. if isStruct = ft.Kind() == reflect.Struct; !isStruct {
  171. if c.converter(ft) == nil && builtinConverters[ft.Kind()] == nil {
  172. // Type is not supported.
  173. return
  174. }
  175. }
  176. info.fields = append(info.fields, &fieldInfo{
  177. typ: field.Type,
  178. name: field.Name,
  179. alias: alias,
  180. unmarshalerInfo: m,
  181. isSliceOfStructs: isSlice && isStruct,
  182. isAnonymous: field.Anonymous,
  183. isRequired: options.Contains("required"),
  184. })
  185. }
  186. // converter returns the converter for a type.
  187. func (c *cache) converter(t reflect.Type) Converter {
  188. return c.regconv[t]
  189. }
  190. // ----------------------------------------------------------------------------
  191. type structInfo struct {
  192. fields []*fieldInfo
  193. }
  194. func (i *structInfo) get(alias string) *fieldInfo {
  195. for _, field := range i.fields {
  196. if strings.EqualFold(field.alias, alias) {
  197. return field
  198. }
  199. }
  200. return nil
  201. }
  202. type fieldInfo struct {
  203. typ reflect.Type
  204. // name is the field name in the struct.
  205. name string
  206. alias string
  207. // unmarshalerInfo contains information regarding the
  208. // encoding.TextUnmarshaler implementation of the field type.
  209. unmarshalerInfo unmarshaler
  210. // isSliceOfStructs indicates if the field type is a slice of structs.
  211. isSliceOfStructs bool
  212. // isAnonymous indicates whether the field is embedded in the struct.
  213. isAnonymous bool
  214. isRequired bool
  215. }
  216. type pathPart struct {
  217. field *fieldInfo
  218. path []string // path to the field: walks structs using field names.
  219. index int // struct index in slices of structs.
  220. }
  221. // ----------------------------------------------------------------------------
  222. // fieldAlias parses a field tag to get a field alias.
  223. func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
  224. if tag := field.Tag.Get(tagName); tag != "" {
  225. alias, options = parseTag(tag)
  226. }
  227. if alias == "" {
  228. alias = field.Name
  229. }
  230. return alias, options
  231. }
  232. // tagOptions is the string following a comma in a struct field's tag, or
  233. // the empty string. It does not include the leading comma.
  234. type tagOptions []string
  235. // parseTag splits a struct field's url tag into its name and comma-separated
  236. // options.
  237. func parseTag(tag string) (string, tagOptions) {
  238. s := strings.Split(tag, ",")
  239. return s[0], s[1:]
  240. }
  241. // Contains checks whether the tagOptions contains the specified option.
  242. func (o tagOptions) Contains(option string) bool {
  243. for _, s := range o {
  244. if s == option {
  245. return true
  246. }
  247. }
  248. return false
  249. }