cache.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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 errInvalidPath = 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, errInvalidPath
  50. }
  51. if struc = c.get(t); struc == nil {
  52. return nil, errInvalidPath
  53. }
  54. if field = struc.get(keys[i]); field == nil {
  55. return nil, errInvalidPath
  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, errInvalidPath
  69. }
  70. if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil {
  71. return nil, errInvalidPath
  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, "")
  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, parentAlias string) *structInfo {
  120. info := &structInfo{}
  121. var anonymousInfos []*structInfo
  122. for i := 0; i < t.NumField(); i++ {
  123. if f := c.createField(t.Field(i), parentAlias); f != nil {
  124. info.fields = append(info.fields, f)
  125. if ft := indirectType(f.typ); ft.Kind() == reflect.Struct && f.isAnonymous {
  126. anonymousInfos = append(anonymousInfos, c.create(ft, f.canonicalAlias))
  127. }
  128. }
  129. }
  130. for i, a := range anonymousInfos {
  131. others := []*structInfo{info}
  132. others = append(others, anonymousInfos[:i]...)
  133. others = append(others, anonymousInfos[i+1:]...)
  134. for _, f := range a.fields {
  135. if !containsAlias(others, f.alias) {
  136. info.fields = append(info.fields, f)
  137. }
  138. }
  139. }
  140. return info
  141. }
  142. // createField creates a fieldInfo for the given field.
  143. func (c *cache) createField(field reflect.StructField, parentAlias string) *fieldInfo {
  144. alias, options := fieldAlias(field, c.tag)
  145. if alias == "-" {
  146. // Ignore this field.
  147. return nil
  148. }
  149. canonicalAlias := alias
  150. if parentAlias != "" {
  151. canonicalAlias = parentAlias + "." + alias
  152. }
  153. // Check if the type is supported and don't cache it if not.
  154. // First let's get the basic type.
  155. isSlice, isStruct := false, false
  156. ft := field.Type
  157. m := isTextUnmarshaler(reflect.Zero(ft))
  158. if ft.Kind() == reflect.Ptr {
  159. ft = ft.Elem()
  160. }
  161. if isSlice = ft.Kind() == reflect.Slice; isSlice {
  162. ft = ft.Elem()
  163. if ft.Kind() == reflect.Ptr {
  164. ft = ft.Elem()
  165. }
  166. }
  167. if ft.Kind() == reflect.Array {
  168. ft = ft.Elem()
  169. if ft.Kind() == reflect.Ptr {
  170. ft = ft.Elem()
  171. }
  172. }
  173. if isStruct = ft.Kind() == reflect.Struct; !isStruct {
  174. if c.converter(ft) == nil && builtinConverters[ft.Kind()] == nil {
  175. // Type is not supported.
  176. return nil
  177. }
  178. }
  179. return &fieldInfo{
  180. typ: field.Type,
  181. name: field.Name,
  182. alias: alias,
  183. canonicalAlias: canonicalAlias,
  184. unmarshalerInfo: m,
  185. isSliceOfStructs: isSlice && isStruct,
  186. isAnonymous: field.Anonymous,
  187. isRequired: options.Contains("required"),
  188. }
  189. }
  190. // converter returns the converter for a type.
  191. func (c *cache) converter(t reflect.Type) Converter {
  192. return c.regconv[t]
  193. }
  194. // ----------------------------------------------------------------------------
  195. type structInfo struct {
  196. fields []*fieldInfo
  197. }
  198. func (i *structInfo) get(alias string) *fieldInfo {
  199. for _, field := range i.fields {
  200. if strings.EqualFold(field.alias, alias) {
  201. return field
  202. }
  203. }
  204. return nil
  205. }
  206. func containsAlias(infos []*structInfo, alias string) bool {
  207. for _, info := range infos {
  208. if info.get(alias) != nil {
  209. return true
  210. }
  211. }
  212. return false
  213. }
  214. type fieldInfo struct {
  215. typ reflect.Type
  216. // name is the field name in the struct.
  217. name string
  218. alias string
  219. // canonicalAlias is almost the same as the alias, but is prefixed with
  220. // an embedded struct field alias in dotted notation if this field is
  221. // promoted from the struct.
  222. // For instance, if the alias is "N" and this field is an embedded field
  223. // in a struct "X", canonicalAlias will be "X.N".
  224. canonicalAlias string
  225. // unmarshalerInfo contains information regarding the
  226. // encoding.TextUnmarshaler implementation of the field type.
  227. unmarshalerInfo unmarshaler
  228. // isSliceOfStructs indicates if the field type is a slice of structs.
  229. isSliceOfStructs bool
  230. // isAnonymous indicates whether the field is embedded in the struct.
  231. isAnonymous bool
  232. isRequired bool
  233. }
  234. func (f *fieldInfo) paths(prefix string) []string {
  235. if f.alias == f.canonicalAlias {
  236. return []string{prefix + f.alias}
  237. }
  238. return []string{prefix + f.alias, prefix + f.canonicalAlias}
  239. }
  240. type pathPart struct {
  241. field *fieldInfo
  242. path []string // path to the field: walks structs using field names.
  243. index int // struct index in slices of structs.
  244. }
  245. // ----------------------------------------------------------------------------
  246. func indirectType(typ reflect.Type) reflect.Type {
  247. if typ.Kind() == reflect.Ptr {
  248. return typ.Elem()
  249. }
  250. return typ
  251. }
  252. // fieldAlias parses a field tag to get a field alias.
  253. func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
  254. if tag := field.Tag.Get(tagName); tag != "" {
  255. alias, options = parseTag(tag)
  256. }
  257. if alias == "" {
  258. alias = field.Name
  259. }
  260. return alias, options
  261. }
  262. // tagOptions is the string following a comma in a struct field's tag, or
  263. // the empty string. It does not include the leading comma.
  264. type tagOptions []string
  265. // parseTag splits a struct field's url tag into its name and comma-separated
  266. // options.
  267. func parseTag(tag string) (string, tagOptions) {
  268. s := strings.Split(tag, ",")
  269. return s[0], s[1:]
  270. }
  271. // Contains checks whether the tagOptions contains the specified option.
  272. func (o tagOptions) Contains(option string) bool {
  273. for _, s := range o {
  274. if s == option {
  275. return true
  276. }
  277. }
  278. return false
  279. }