You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pointer.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // author sigu-399
  15. // author-github https://github.com/sigu-399
  16. // author-mail sigu.399@gmail.com
  17. //
  18. // repository-name jsonpointer
  19. // repository-desc An implementation of JSON Pointer - Go language
  20. //
  21. // description Main and unique file.
  22. //
  23. // created 25-02-2013
  24. package jsonpointer
  25. import (
  26. "errors"
  27. "fmt"
  28. "reflect"
  29. "strconv"
  30. "strings"
  31. "github.com/go-openapi/swag"
  32. )
  33. const (
  34. emptyPointer = ``
  35. pointerSeparator = `/`
  36. invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator
  37. )
  38. var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
  39. var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
  40. // JSONPointable is an interface for structs to implement when they need to customize the
  41. // json pointer process
  42. type JSONPointable interface {
  43. JSONLookup(string) (interface{}, error)
  44. }
  45. // JSONSetable is an interface for structs to implement when they need to customize the
  46. // json pointer process
  47. type JSONSetable interface {
  48. JSONSet(string, interface{}) error
  49. }
  50. // New creates a new json pointer for the given string
  51. func New(jsonPointerString string) (Pointer, error) {
  52. var p Pointer
  53. err := p.parse(jsonPointerString)
  54. return p, err
  55. }
  56. // Pointer the json pointer reprsentation
  57. type Pointer struct {
  58. referenceTokens []string
  59. }
  60. // "Constructor", parses the given string JSON pointer
  61. func (p *Pointer) parse(jsonPointerString string) error {
  62. var err error
  63. if jsonPointerString != emptyPointer {
  64. if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
  65. err = errors.New(invalidStart)
  66. } else {
  67. referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
  68. for _, referenceToken := range referenceTokens[1:] {
  69. p.referenceTokens = append(p.referenceTokens, referenceToken)
  70. }
  71. }
  72. }
  73. return err
  74. }
  75. // Get uses the pointer to retrieve a value from a JSON document
  76. func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
  77. return p.get(document, swag.DefaultJSONNameProvider)
  78. }
  79. // Set uses the pointer to set a value from a JSON document
  80. func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) {
  81. return document, p.set(document, value, swag.DefaultJSONNameProvider)
  82. }
  83. // GetForToken gets a value for a json pointer token 1 level deep
  84. func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) {
  85. return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
  86. }
  87. // SetForToken gets a value for a json pointer token 1 level deep
  88. func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) {
  89. return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
  90. }
  91. func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
  92. rValue := reflect.Indirect(reflect.ValueOf(node))
  93. kind := rValue.Kind()
  94. switch kind {
  95. case reflect.Struct:
  96. if rValue.Type().Implements(jsonPointableType) {
  97. r, err := node.(JSONPointable).JSONLookup(decodedToken)
  98. if err != nil {
  99. return nil, kind, err
  100. }
  101. return r, kind, nil
  102. }
  103. nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
  104. if !ok {
  105. return nil, kind, fmt.Errorf("object has no field %q", decodedToken)
  106. }
  107. fld := rValue.FieldByName(nm)
  108. return fld.Interface(), kind, nil
  109. case reflect.Map:
  110. kv := reflect.ValueOf(decodedToken)
  111. mv := rValue.MapIndex(kv)
  112. if mv.IsValid() {
  113. return mv.Interface(), kind, nil
  114. }
  115. return nil, kind, fmt.Errorf("object has no key %q", decodedToken)
  116. case reflect.Slice:
  117. tokenIndex, err := strconv.Atoi(decodedToken)
  118. if err != nil {
  119. return nil, kind, err
  120. }
  121. sLength := rValue.Len()
  122. if tokenIndex < 0 || tokenIndex >= sLength {
  123. return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
  124. }
  125. elem := rValue.Index(tokenIndex)
  126. return elem.Interface(), kind, nil
  127. default:
  128. return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken)
  129. }
  130. }
  131. func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error {
  132. rValue := reflect.Indirect(reflect.ValueOf(node))
  133. switch rValue.Kind() {
  134. case reflect.Struct:
  135. if ns, ok := node.(JSONSetable); ok { // pointer impl
  136. return ns.JSONSet(decodedToken, data)
  137. }
  138. if rValue.Type().Implements(jsonSetableType) {
  139. return node.(JSONSetable).JSONSet(decodedToken, data)
  140. }
  141. nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
  142. if !ok {
  143. return fmt.Errorf("object has no field %q", decodedToken)
  144. }
  145. fld := rValue.FieldByName(nm)
  146. if fld.IsValid() {
  147. fld.Set(reflect.ValueOf(data))
  148. }
  149. return nil
  150. case reflect.Map:
  151. kv := reflect.ValueOf(decodedToken)
  152. rValue.SetMapIndex(kv, reflect.ValueOf(data))
  153. return nil
  154. case reflect.Slice:
  155. tokenIndex, err := strconv.Atoi(decodedToken)
  156. if err != nil {
  157. return err
  158. }
  159. sLength := rValue.Len()
  160. if tokenIndex < 0 || tokenIndex >= sLength {
  161. return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
  162. }
  163. elem := rValue.Index(tokenIndex)
  164. if !elem.CanSet() {
  165. return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
  166. }
  167. elem.Set(reflect.ValueOf(data))
  168. return nil
  169. default:
  170. return fmt.Errorf("invalid token reference %q", decodedToken)
  171. }
  172. }
  173. func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
  174. if nameProvider == nil {
  175. nameProvider = swag.DefaultJSONNameProvider
  176. }
  177. kind := reflect.Invalid
  178. // Full document when empty
  179. if len(p.referenceTokens) == 0 {
  180. return node, kind, nil
  181. }
  182. for _, token := range p.referenceTokens {
  183. decodedToken := Unescape(token)
  184. r, knd, err := getSingleImpl(node, decodedToken, nameProvider)
  185. if err != nil {
  186. return nil, knd, err
  187. }
  188. node, kind = r, knd
  189. }
  190. rValue := reflect.ValueOf(node)
  191. kind = rValue.Kind()
  192. return node, kind, nil
  193. }
  194. func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error {
  195. knd := reflect.ValueOf(node).Kind()
  196. if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
  197. return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
  198. }
  199. if nameProvider == nil {
  200. nameProvider = swag.DefaultJSONNameProvider
  201. }
  202. // Full document when empty
  203. if len(p.referenceTokens) == 0 {
  204. return nil
  205. }
  206. lastI := len(p.referenceTokens) - 1
  207. for i, token := range p.referenceTokens {
  208. isLastToken := i == lastI
  209. decodedToken := Unescape(token)
  210. if isLastToken {
  211. return setSingleImpl(node, data, decodedToken, nameProvider)
  212. }
  213. rValue := reflect.Indirect(reflect.ValueOf(node))
  214. kind := rValue.Kind()
  215. switch kind {
  216. case reflect.Struct:
  217. if rValue.Type().Implements(jsonPointableType) {
  218. r, err := node.(JSONPointable).JSONLookup(decodedToken)
  219. if err != nil {
  220. return err
  221. }
  222. fld := reflect.ValueOf(r)
  223. if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
  224. node = fld.Addr().Interface()
  225. continue
  226. }
  227. node = r
  228. continue
  229. }
  230. nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
  231. if !ok {
  232. return fmt.Errorf("object has no field %q", decodedToken)
  233. }
  234. fld := rValue.FieldByName(nm)
  235. if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
  236. node = fld.Addr().Interface()
  237. continue
  238. }
  239. node = fld.Interface()
  240. case reflect.Map:
  241. kv := reflect.ValueOf(decodedToken)
  242. mv := rValue.MapIndex(kv)
  243. if !mv.IsValid() {
  244. return fmt.Errorf("object has no key %q", decodedToken)
  245. }
  246. if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
  247. node = mv.Addr().Interface()
  248. continue
  249. }
  250. node = mv.Interface()
  251. case reflect.Slice:
  252. tokenIndex, err := strconv.Atoi(decodedToken)
  253. if err != nil {
  254. return err
  255. }
  256. sLength := rValue.Len()
  257. if tokenIndex < 0 || tokenIndex >= sLength {
  258. return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
  259. }
  260. elem := rValue.Index(tokenIndex)
  261. if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
  262. node = elem.Addr().Interface()
  263. continue
  264. }
  265. node = elem.Interface()
  266. default:
  267. return fmt.Errorf("invalid token reference %q", decodedToken)
  268. }
  269. }
  270. return nil
  271. }
  272. // DecodedTokens returns the decoded tokens
  273. func (p *Pointer) DecodedTokens() []string {
  274. result := make([]string, 0, len(p.referenceTokens))
  275. for _, t := range p.referenceTokens {
  276. result = append(result, Unescape(t))
  277. }
  278. return result
  279. }
  280. // IsEmpty returns true if this is an empty json pointer
  281. // this indicates that it points to the root document
  282. func (p *Pointer) IsEmpty() bool {
  283. return len(p.referenceTokens) == 0
  284. }
  285. // Pointer to string representation function
  286. func (p *Pointer) String() string {
  287. if len(p.referenceTokens) == 0 {
  288. return emptyPointer
  289. }
  290. pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
  291. return pointerString
  292. }
  293. // Specific JSON pointer encoding here
  294. // ~0 => ~
  295. // ~1 => /
  296. // ... and vice versa
  297. const (
  298. encRefTok0 = `~0`
  299. encRefTok1 = `~1`
  300. decRefTok0 = `~`
  301. decRefTok1 = `/`
  302. )
  303. // Unescape unescapes a json pointer reference token string to the original representation
  304. func Unescape(token string) string {
  305. step1 := strings.Replace(token, encRefTok1, decRefTok1, -1)
  306. step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1)
  307. return step2
  308. }
  309. // Escape escapes a pointer reference token string
  310. func Escape(token string) string {
  311. step1 := strings.Replace(token, decRefTok0, encRefTok0, -1)
  312. step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1)
  313. return step2
  314. }