blob: fe2d6ee574f18959709d7897dd914170ae1617c0 [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001// 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
15// author sigu-399
16// author-github https://github.com/sigu-399
17// author-mail sigu.399@gmail.com
18//
19// repository-name jsonpointer
20// repository-desc An implementation of JSON Pointer - Go language
21//
22// description Main and unique file.
23//
24// created 25-02-2013
25
26package jsonpointer
27
28import (
29 "errors"
30 "fmt"
31 "reflect"
32 "strconv"
33 "strings"
34
35 "github.com/go-openapi/swag"
36)
37
38const (
39 emptyPointer = ``
40 pointerSeparator = `/`
41
42 invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator
43)
44
45var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
46var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
47
48// JSONPointable is an interface for structs to implement when they need to customize the
49// json pointer process
50type JSONPointable interface {
51 JSONLookup(string) (interface{}, error)
52}
53
54// JSONSetable is an interface for structs to implement when they need to customize the
55// json pointer process
56type JSONSetable interface {
57 JSONSet(string, interface{}) error
58}
59
60// New creates a new json pointer for the given string
61func New(jsonPointerString string) (Pointer, error) {
62
63 var p Pointer
64 err := p.parse(jsonPointerString)
65 return p, err
66
67}
68
69// Pointer the json pointer reprsentation
70type Pointer struct {
71 referenceTokens []string
72}
73
74// "Constructor", parses the given string JSON pointer
75func (p *Pointer) parse(jsonPointerString string) error {
76
77 var err error
78
79 if jsonPointerString != emptyPointer {
80 if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
81 err = errors.New(invalidStart)
82 } else {
83 referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
84 for _, referenceToken := range referenceTokens[1:] {
85 p.referenceTokens = append(p.referenceTokens, referenceToken)
86 }
87 }
88 }
89
90 return err
91}
92
93// Get uses the pointer to retrieve a value from a JSON document
94func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
95 return p.get(document, swag.DefaultJSONNameProvider)
96}
97
98// Set uses the pointer to set a value from a JSON document
99func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) {
100 return document, p.set(document, value, swag.DefaultJSONNameProvider)
101}
102
103// GetForToken gets a value for a json pointer token 1 level deep
104func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) {
105 return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
106}
107
108// SetForToken gets a value for a json pointer token 1 level deep
109func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) {
110 return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
111}
112
113func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
114 rValue := reflect.Indirect(reflect.ValueOf(node))
115 kind := rValue.Kind()
116
117 switch kind {
118
119 case reflect.Struct:
120 if rValue.Type().Implements(jsonPointableType) {
121 r, err := node.(JSONPointable).JSONLookup(decodedToken)
122 if err != nil {
123 return nil, kind, err
124 }
125 return r, kind, nil
126 }
127 nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
128 if !ok {
129 return nil, kind, fmt.Errorf("object has no field %q", decodedToken)
130 }
131 fld := rValue.FieldByName(nm)
132 return fld.Interface(), kind, nil
133
134 case reflect.Map:
135 kv := reflect.ValueOf(decodedToken)
136 mv := rValue.MapIndex(kv)
137
138 if mv.IsValid() && !swag.IsZero(mv) {
139 return mv.Interface(), kind, nil
140 }
141 return nil, kind, fmt.Errorf("object has no key %q", decodedToken)
142
143 case reflect.Slice:
144 tokenIndex, err := strconv.Atoi(decodedToken)
145 if err != nil {
146 return nil, kind, err
147 }
148 sLength := rValue.Len()
149 if tokenIndex < 0 || tokenIndex >= sLength {
150 return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
151 }
152
153 elem := rValue.Index(tokenIndex)
154 return elem.Interface(), kind, nil
155
156 default:
157 return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken)
158 }
159
160}
161
162func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error {
163 rValue := reflect.Indirect(reflect.ValueOf(node))
164 switch rValue.Kind() {
165
166 case reflect.Struct:
167 if ns, ok := node.(JSONSetable); ok { // pointer impl
168 return ns.JSONSet(decodedToken, data)
169 }
170
171 if rValue.Type().Implements(jsonSetableType) {
172 return node.(JSONSetable).JSONSet(decodedToken, data)
173 }
174
175 nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
176 if !ok {
177 return fmt.Errorf("object has no field %q", decodedToken)
178 }
179 fld := rValue.FieldByName(nm)
180 if fld.IsValid() {
181 fld.Set(reflect.ValueOf(data))
182 }
183 return nil
184
185 case reflect.Map:
186 kv := reflect.ValueOf(decodedToken)
187 rValue.SetMapIndex(kv, reflect.ValueOf(data))
188 return nil
189
190 case reflect.Slice:
191 tokenIndex, err := strconv.Atoi(decodedToken)
192 if err != nil {
193 return err
194 }
195 sLength := rValue.Len()
196 if tokenIndex < 0 || tokenIndex >= sLength {
197 return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
198 }
199
200 elem := rValue.Index(tokenIndex)
201 if !elem.CanSet() {
202 return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
203 }
204 elem.Set(reflect.ValueOf(data))
205 return nil
206
207 default:
208 return fmt.Errorf("invalid token reference %q", decodedToken)
209 }
210
211}
212
213func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
214
215 if nameProvider == nil {
216 nameProvider = swag.DefaultJSONNameProvider
217 }
218
219 kind := reflect.Invalid
220
221 // Full document when empty
222 if len(p.referenceTokens) == 0 {
223 return node, kind, nil
224 }
225
226 for _, token := range p.referenceTokens {
227
228 decodedToken := Unescape(token)
229
230 r, knd, err := getSingleImpl(node, decodedToken, nameProvider)
231 if err != nil {
232 return nil, knd, err
233 }
234 node, kind = r, knd
235
236 }
237
238 rValue := reflect.ValueOf(node)
239 kind = rValue.Kind()
240
241 return node, kind, nil
242}
243
244func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error {
245 knd := reflect.ValueOf(node).Kind()
246
247 if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
248 return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
249 }
250
251 if nameProvider == nil {
252 nameProvider = swag.DefaultJSONNameProvider
253 }
254
255 // Full document when empty
256 if len(p.referenceTokens) == 0 {
257 return nil
258 }
259
260 lastI := len(p.referenceTokens) - 1
261 for i, token := range p.referenceTokens {
262 isLastToken := i == lastI
263 decodedToken := Unescape(token)
264
265 if isLastToken {
266
267 return setSingleImpl(node, data, decodedToken, nameProvider)
268 }
269
270 rValue := reflect.Indirect(reflect.ValueOf(node))
271 kind := rValue.Kind()
272
273 switch kind {
274
275 case reflect.Struct:
276 if rValue.Type().Implements(jsonPointableType) {
277 r, err := node.(JSONPointable).JSONLookup(decodedToken)
278 if err != nil {
279 return err
280 }
281 fld := reflect.ValueOf(r)
282 if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
283 node = fld.Addr().Interface()
284 continue
285 }
286 node = r
287 continue
288 }
289 nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
290 if !ok {
291 return fmt.Errorf("object has no field %q", decodedToken)
292 }
293 fld := rValue.FieldByName(nm)
294 if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
295 node = fld.Addr().Interface()
296 continue
297 }
298 node = fld.Interface()
299
300 case reflect.Map:
301 kv := reflect.ValueOf(decodedToken)
302 mv := rValue.MapIndex(kv)
303
304 if !mv.IsValid() {
305 return fmt.Errorf("object has no key %q", decodedToken)
306 }
307 if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
308 node = mv.Addr().Interface()
309 continue
310 }
311 node = mv.Interface()
312
313 case reflect.Slice:
314 tokenIndex, err := strconv.Atoi(decodedToken)
315 if err != nil {
316 return err
317 }
318 sLength := rValue.Len()
319 if tokenIndex < 0 || tokenIndex >= sLength {
320 return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
321 }
322
323 elem := rValue.Index(tokenIndex)
324 if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
325 node = elem.Addr().Interface()
326 continue
327 }
328 node = elem.Interface()
329
330 default:
331 return fmt.Errorf("invalid token reference %q", decodedToken)
332 }
333
334 }
335
336 return nil
337}
338
339// DecodedTokens returns the decoded tokens
340func (p *Pointer) DecodedTokens() []string {
341 result := make([]string, 0, len(p.referenceTokens))
342 for _, t := range p.referenceTokens {
343 result = append(result, Unescape(t))
344 }
345 return result
346}
347
348// IsEmpty returns true if this is an empty json pointer
349// this indicates that it points to the root document
350func (p *Pointer) IsEmpty() bool {
351 return len(p.referenceTokens) == 0
352}
353
354// Pointer to string representation function
355func (p *Pointer) String() string {
356
357 if len(p.referenceTokens) == 0 {
358 return emptyPointer
359 }
360
361 pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
362
363 return pointerString
364}
365
366// Specific JSON pointer encoding here
367// ~0 => ~
368// ~1 => /
369// ... and vice versa
370
371const (
372 encRefTok0 = `~0`
373 encRefTok1 = `~1`
374 decRefTok0 = `~`
375 decRefTok1 = `/`
376)
377
378// Unescape unescapes a json pointer reference token string to the original representation
379func Unescape(token string) string {
380 step1 := strings.Replace(token, encRefTok1, decRefTok1, -1)
381 step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1)
382 return step2
383}
384
385// Escape escapes a pointer reference token string
386func Escape(token string) string {
387 step1 := strings.Replace(token, decRefTok0, encRefTok0, -1)
388 step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1)
389 return step2
390}