pointer.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. // JSONPointable is an interface for structs to implement when they need to customize the
  40. // json pointer process
  41. type JSONPointable interface {
  42. JSONLookup(string) (interface{}, error)
  43. }
  44. type implStruct struct {
  45. mode string // "SET" or "GET"
  46. inDocument interface{}
  47. setInValue interface{}
  48. getOutNode interface{}
  49. getOutKind reflect.Kind
  50. outError error
  51. }
  52. // New creates a new json pointer for the given string
  53. func New(jsonPointerString string) (Pointer, error) {
  54. var p Pointer
  55. err := p.parse(jsonPointerString)
  56. return p, err
  57. }
  58. // Pointer the json pointer reprsentation
  59. type Pointer struct {
  60. referenceTokens []string
  61. }
  62. // "Constructor", parses the given string JSON pointer
  63. func (p *Pointer) parse(jsonPointerString string) error {
  64. var err error
  65. if jsonPointerString != emptyPointer {
  66. if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
  67. err = errors.New(invalidStart)
  68. } else {
  69. referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
  70. for _, referenceToken := range referenceTokens[1:] {
  71. p.referenceTokens = append(p.referenceTokens, referenceToken)
  72. }
  73. }
  74. }
  75. return err
  76. }
  77. // Get uses the pointer to retrieve a value from a JSON document
  78. func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
  79. return p.get(document, swag.DefaultJSONNameProvider)
  80. }
  81. // GetForToken gets a value for a json pointer token 1 level deep
  82. func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) {
  83. return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
  84. }
  85. func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
  86. kind := reflect.Invalid
  87. rValue := reflect.Indirect(reflect.ValueOf(node))
  88. kind = rValue.Kind()
  89. switch kind {
  90. case reflect.Struct:
  91. if rValue.Type().Implements(jsonPointableType) {
  92. r, err := node.(JSONPointable).JSONLookup(decodedToken)
  93. if err != nil {
  94. return nil, kind, err
  95. }
  96. return r, kind, nil
  97. }
  98. nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
  99. if !ok {
  100. return nil, kind, fmt.Errorf("object has no field %q", decodedToken)
  101. }
  102. fld := rValue.FieldByName(nm)
  103. return fld.Interface(), kind, nil
  104. case reflect.Map:
  105. kv := reflect.ValueOf(decodedToken)
  106. mv := rValue.MapIndex(kv)
  107. if mv.IsValid() && !swag.IsZero(mv) {
  108. return mv.Interface(), kind, nil
  109. }
  110. return nil, kind, fmt.Errorf("object has no key %q", decodedToken)
  111. case reflect.Slice:
  112. tokenIndex, err := strconv.Atoi(decodedToken)
  113. if err != nil {
  114. return nil, kind, err
  115. }
  116. sLength := rValue.Len()
  117. if tokenIndex < 0 || tokenIndex >= sLength {
  118. return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
  119. }
  120. elem := rValue.Index(tokenIndex)
  121. return elem.Interface(), kind, nil
  122. default:
  123. return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken)
  124. }
  125. }
  126. func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
  127. if nameProvider == nil {
  128. nameProvider = swag.DefaultJSONNameProvider
  129. }
  130. kind := reflect.Invalid
  131. // Full document when empty
  132. if len(p.referenceTokens) == 0 {
  133. return node, kind, nil
  134. }
  135. for _, token := range p.referenceTokens {
  136. decodedToken := Unescape(token)
  137. r, knd, err := getSingleImpl(node, decodedToken, nameProvider)
  138. if err != nil {
  139. return nil, knd, err
  140. }
  141. node, kind = r, knd
  142. }
  143. rValue := reflect.ValueOf(node)
  144. kind = rValue.Kind()
  145. return node, kind, nil
  146. }
  147. // DecodedTokens returns the decoded tokens
  148. func (p *Pointer) DecodedTokens() []string {
  149. result := make([]string, 0, len(p.referenceTokens))
  150. for _, t := range p.referenceTokens {
  151. result = append(result, Unescape(t))
  152. }
  153. return result
  154. }
  155. // IsEmpty returns true if this is an empty json pointer
  156. // this indicates that it points to the root document
  157. func (p *Pointer) IsEmpty() bool {
  158. return len(p.referenceTokens) == 0
  159. }
  160. // Pointer to string representation function
  161. func (p *Pointer) String() string {
  162. if len(p.referenceTokens) == 0 {
  163. return emptyPointer
  164. }
  165. pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
  166. return pointerString
  167. }
  168. // Specific JSON pointer encoding here
  169. // ~0 => ~
  170. // ~1 => /
  171. // ... and vice versa
  172. const (
  173. encRefTok0 = `~0`
  174. encRefTok1 = `~1`
  175. decRefTok0 = `~`
  176. decRefTok1 = `/`
  177. )
  178. // Unescape unescapes a json pointer reference token string to the original representation
  179. func Unescape(token string) string {
  180. step1 := strings.Replace(token, encRefTok1, decRefTok1, -1)
  181. step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1)
  182. return step2
  183. }
  184. // Escape escapes a pointer reference token string
  185. func Escape(token string) string {
  186. step1 := strings.Replace(token, decRefTok0, encRefTok0, -1)
  187. step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1)
  188. return step2
  189. }