cache.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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. const maxParserIndex = 1000
  13. var (
  14. errInvalidPath = errors.New("schema: invalid path")
  15. errIndexTooLarge = errors.New("schema: index exceeds parser limit")
  16. )
  17. // newCache returns a new cache.
  18. func newCache() *cache {
  19. c := cache{
  20. m: make(map[reflect.Type]*structInfo),
  21. regconv: make(map[reflect.Type]Converter),
  22. tag: "schema",
  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. regconv map[reflect.Type]Converter
  31. tag string
  32. }
  33. // registerConverter registers a converter function for a custom type.
  34. func (c *cache) registerConverter(value interface{}, converterFunc Converter) {
  35. c.regconv[reflect.TypeOf(value)] = converterFunc
  36. }
  37. // parsePath parses a path in dotted notation verifying that it is a valid
  38. // path to a struct field.
  39. //
  40. // It returns "path parts" which contain indices to fields to be used by
  41. // reflect.Value.FieldByString(). Multiple parts are required for slices of
  42. // structs.
  43. func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) {
  44. var struc *structInfo
  45. var field *fieldInfo
  46. var index64 int64
  47. var err error
  48. parts := make([]pathPart, 0)
  49. path := make([]string, 0)
  50. keys := strings.Split(p, ".")
  51. for i := 0; i < len(keys); i++ {
  52. if t.Kind() != reflect.Struct {
  53. return nil, errInvalidPath
  54. }
  55. if struc = c.get(t); struc == nil {
  56. return nil, errInvalidPath
  57. }
  58. if field = struc.get(keys[i]); field == nil {
  59. return nil, errInvalidPath
  60. }
  61. // Valid field. Append index.
  62. path = append(path, field.name)
  63. if field.isSliceOfStructs && (!field.unmarshalerInfo.IsValid || (field.unmarshalerInfo.IsValid && field.unmarshalerInfo.IsSliceElement)) {
  64. // Parse a special case: slices of structs.
  65. // i+1 must be the slice index.
  66. //
  67. // Now that struct can implements TextUnmarshaler interface,
  68. // we don't need to force the struct's fields to appear in the path.
  69. // So checking i+2 is not necessary anymore.
  70. i++
  71. if i+1 > len(keys) {
  72. return nil, errInvalidPath
  73. }
  74. if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil {
  75. return nil, errInvalidPath
  76. }
  77. if index64 > maxParserIndex {
  78. return nil, errIndexTooLarge
  79. }
  80. parts = append(parts, pathPart{
  81. path: path,
  82. field: field,
  83. index: int(index64),
  84. })
  85. path = make([]string, 0)
  86. // Get the next struct type, dropping ptrs.
  87. if field.typ.Kind() == reflect.Ptr {
  88. t = field.typ.Elem()
  89. } else {
  90. t = field.typ
  91. }
  92. if t.Kind() == reflect.Slice {
  93. t = t.Elem()
  94. if t.Kind() == reflect.Ptr {
  95. t = t.Elem()
  96. }
  97. }
  98. } else if field.typ.Kind() == reflect.Ptr {
  99. t = field.typ.Elem()
  100. } else {
  101. t = field.typ
  102. }
  103. }
  104. // Add the remaining.
  105. parts = append(parts, pathPart{
  106. path: path,
  107. field: field,
  108. index: -1,
  109. })
  110. return parts, nil
  111. }
  112. // get returns a cached structInfo, creating it if necessary.
  113. func (c *cache) get(t reflect.Type) *structInfo {
  114. c.l.RLock()
  115. info := c.m[t]
  116. c.l.RUnlock()
  117. if info == nil {
  118. info = c.create(t, "")
  119. c.l.Lock()
  120. c.m[t] = info
  121. c.l.Unlock()
  122. }
  123. return info
  124. }
  125. // create creates a structInfo with meta-data about a struct.
  126. func (c *cache) create(t reflect.Type, parentAlias string) *structInfo {
  127. info := &structInfo{}
  128. var anonymousInfos []*structInfo
  129. for i := 0; i < t.NumField(); i++ {
  130. if f := c.createField(t.Field(i), parentAlias); f != nil {
  131. info.fields = append(info.fields, f)
  132. if ft := indirectType(f.typ); ft.Kind() == reflect.Struct && f.isAnonymous {
  133. anonymousInfos = append(anonymousInfos, c.create(ft, f.canonicalAlias))
  134. }
  135. }
  136. }
  137. for i, a := range anonymousInfos {
  138. others := []*structInfo{info}
  139. others = append(others, anonymousInfos[:i]...)
  140. others = append(others, anonymousInfos[i+1:]...)
  141. for _, f := range a.fields {
  142. if !containsAlias(others, f.alias) {
  143. info.fields = append(info.fields, f)
  144. }
  145. }
  146. }
  147. return info
  148. }
  149. // createField creates a fieldInfo for the given field.
  150. func (c *cache) createField(field reflect.StructField, parentAlias string) *fieldInfo {
  151. alias, options := fieldAlias(field, c.tag)
  152. if alias == "-" {
  153. // Ignore this field.
  154. return nil
  155. }
  156. canonicalAlias := alias
  157. if parentAlias != "" {
  158. canonicalAlias = parentAlias + "." + alias
  159. }
  160. // Check if the type is supported and don't cache it if not.
  161. // First let's get the basic type.
  162. isSlice, isStruct := false, false
  163. ft := field.Type
  164. m := isTextUnmarshaler(reflect.Zero(ft))
  165. if ft.Kind() == reflect.Ptr {
  166. ft = ft.Elem()
  167. }
  168. if isSlice = ft.Kind() == reflect.Slice; isSlice {
  169. ft = ft.Elem()
  170. if ft.Kind() == reflect.Ptr {
  171. ft = ft.Elem()
  172. }
  173. }
  174. if ft.Kind() == reflect.Array {
  175. ft = ft.Elem()
  176. if ft.Kind() == reflect.Ptr {
  177. ft = ft.Elem()
  178. }
  179. }
  180. if isStruct = ft.Kind() == reflect.Struct; !isStruct {
  181. if c.converter(ft) == nil && builtinConverters[ft.Kind()] == nil {
  182. // Type is not supported.
  183. return nil
  184. }
  185. }
  186. return &fieldInfo{
  187. typ: field.Type,
  188. name: field.Name,
  189. alias: alias,
  190. canonicalAlias: canonicalAlias,
  191. unmarshalerInfo: m,
  192. isSliceOfStructs: isSlice && isStruct,
  193. isAnonymous: field.Anonymous,
  194. isRequired: options.Contains("required"),
  195. }
  196. }
  197. // converter returns the converter for a type.
  198. func (c *cache) converter(t reflect.Type) Converter {
  199. return c.regconv[t]
  200. }
  201. // ----------------------------------------------------------------------------
  202. type structInfo struct {
  203. fields []*fieldInfo
  204. }
  205. func (i *structInfo) get(alias string) *fieldInfo {
  206. for _, field := range i.fields {
  207. if strings.EqualFold(field.alias, alias) {
  208. return field
  209. }
  210. }
  211. return nil
  212. }
  213. func containsAlias(infos []*structInfo, alias string) bool {
  214. for _, info := range infos {
  215. if info.get(alias) != nil {
  216. return true
  217. }
  218. }
  219. return false
  220. }
  221. type fieldInfo struct {
  222. typ reflect.Type
  223. // name is the field name in the struct.
  224. name string
  225. alias string
  226. // canonicalAlias is almost the same as the alias, but is prefixed with
  227. // an embedded struct field alias in dotted notation if this field is
  228. // promoted from the struct.
  229. // For instance, if the alias is "N" and this field is an embedded field
  230. // in a struct "X", canonicalAlias will be "X.N".
  231. canonicalAlias string
  232. // unmarshalerInfo contains information regarding the
  233. // encoding.TextUnmarshaler implementation of the field type.
  234. unmarshalerInfo unmarshaler
  235. // isSliceOfStructs indicates if the field type is a slice of structs.
  236. isSliceOfStructs bool
  237. // isAnonymous indicates whether the field is embedded in the struct.
  238. isAnonymous bool
  239. isRequired bool
  240. }
  241. func (f *fieldInfo) paths(prefix string) []string {
  242. if f.alias == f.canonicalAlias {
  243. return []string{prefix + f.alias}
  244. }
  245. return []string{prefix + f.alias, prefix + f.canonicalAlias}
  246. }
  247. type pathPart struct {
  248. field *fieldInfo
  249. path []string // path to the field: walks structs using field names.
  250. index int // struct index in slices of structs.
  251. }
  252. // ----------------------------------------------------------------------------
  253. func indirectType(typ reflect.Type) reflect.Type {
  254. if typ.Kind() == reflect.Ptr {
  255. return typ.Elem()
  256. }
  257. return typ
  258. }
  259. // fieldAlias parses a field tag to get a field alias.
  260. func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
  261. if tag := field.Tag.Get(tagName); tag != "" {
  262. alias, options = parseTag(tag)
  263. }
  264. if alias == "" {
  265. alias = field.Name
  266. }
  267. return alias, options
  268. }
  269. // tagOptions is the string following a comma in a struct field's tag, or
  270. // the empty string. It does not include the leading comma.
  271. type tagOptions []string
  272. // parseTag splits a struct field's url tag into its name and comma-separated
  273. // options.
  274. func parseTag(tag string) (string, tagOptions) {
  275. s := strings.Split(tag, ",")
  276. return s[0], s[1:]
  277. }
  278. // Contains checks whether the tagOptions contains the specified option.
  279. func (o tagOptions) Contains(option string) bool {
  280. for _, s := range o {
  281. if s == option {
  282. return true
  283. }
  284. }
  285. return false
  286. }