Tizen_4.0 base
[platform/upstream/docker-engine.git] / vendor / github.com / xeipuuv / gojsonpointer / pointer.go
1 // Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
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                       xeipuuv
16 // author-github        https://github.com/xeipuuv
17 // author-mail          xeipuuv@gmail.com
18 //
19 // repository-name      gojsonpointer
20 // repository-desc      An implementation of JSON Pointer - Go language
21 //
22 // description          Main and unique file.
23 //
24 // created              25-02-2013
25
26 package gojsonpointer
27
28 import (
29         "errors"
30         "fmt"
31         "reflect"
32         "strconv"
33         "strings"
34 )
35
36 const (
37         const_empty_pointer     = ``
38         const_pointer_separator = `/`
39
40         const_invalid_start = `JSON pointer must be empty or start with a "` + const_pointer_separator + `"`
41 )
42
43 type implStruct struct {
44         mode string // "SET" or "GET"
45
46         inDocument interface{}
47
48         setInValue interface{}
49
50         getOutNode interface{}
51         getOutKind reflect.Kind
52         outError   error
53 }
54
55 func NewJsonPointer(jsonPointerString string) (JsonPointer, error) {
56
57         var p JsonPointer
58         err := p.parse(jsonPointerString)
59         return p, err
60
61 }
62
63 type JsonPointer struct {
64         referenceTokens []string
65 }
66
67 // "Constructor", parses the given string JSON pointer
68 func (p *JsonPointer) parse(jsonPointerString string) error {
69
70         var err error
71
72         if jsonPointerString != const_empty_pointer {
73                 if !strings.HasPrefix(jsonPointerString, const_pointer_separator) {
74                         err = errors.New(const_invalid_start)
75                 } else {
76                         referenceTokens := strings.Split(jsonPointerString, const_pointer_separator)
77                         for _, referenceToken := range referenceTokens[1:] {
78                                 p.referenceTokens = append(p.referenceTokens, referenceToken)
79                         }
80                 }
81         }
82
83         return err
84 }
85
86 // Uses the pointer to retrieve a value from a JSON document
87 func (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
88
89         is := &implStruct{mode: "GET", inDocument: document}
90         p.implementation(is)
91         return is.getOutNode, is.getOutKind, is.outError
92
93 }
94
95 // Uses the pointer to update a value from a JSON document
96 func (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error) {
97
98         is := &implStruct{mode: "SET", inDocument: document, setInValue: value}
99         p.implementation(is)
100         return document, is.outError
101
102 }
103
104 // Both Get and Set functions use the same implementation to avoid code duplication
105 func (p *JsonPointer) implementation(i *implStruct) {
106
107         kind := reflect.Invalid
108
109         // Full document when empty
110         if len(p.referenceTokens) == 0 {
111                 i.getOutNode = i.inDocument
112                 i.outError = nil
113                 i.getOutKind = kind
114                 i.outError = nil
115                 return
116         }
117
118         node := i.inDocument
119
120         for ti, token := range p.referenceTokens {
121
122                 decodedToken := decodeReferenceToken(token)
123                 isLastToken := ti == len(p.referenceTokens)-1
124
125                 rValue := reflect.ValueOf(node)
126                 kind = rValue.Kind()
127
128                 switch kind {
129
130                 case reflect.Map:
131                         m := node.(map[string]interface{})
132                         if _, ok := m[decodedToken]; ok {
133                                 node = m[decodedToken]
134                                 if isLastToken && i.mode == "SET" {
135                                         m[decodedToken] = i.setInValue
136                                 }
137                         } else {
138                                 i.outError = errors.New(fmt.Sprintf("Object has no key '%s'", token))
139                                 i.getOutKind = kind
140                                 i.getOutNode = nil
141                                 return
142                         }
143
144                 case reflect.Slice:
145                         s := node.([]interface{})
146                         tokenIndex, err := strconv.Atoi(token)
147                         if err != nil {
148                                 i.outError = errors.New(fmt.Sprintf("Invalid array index '%s'", token))
149                                 i.getOutKind = kind
150                                 i.getOutNode = nil
151                                 return
152                         }
153                         sLength := len(s)
154                         if tokenIndex < 0 || tokenIndex >= sLength {
155                                 i.outError = errors.New(fmt.Sprintf("Out of bound array[0,%d] index '%d'", sLength, tokenIndex))
156                                 i.getOutKind = kind
157                                 i.getOutNode = nil
158                                 return
159                         }
160
161                         node = s[tokenIndex]
162                         if isLastToken && i.mode == "SET" {
163                                 s[tokenIndex] = i.setInValue
164                         }
165
166                 default:
167                         i.outError = errors.New(fmt.Sprintf("Invalid token reference '%s'", token))
168                         i.getOutKind = kind
169                         i.getOutNode = nil
170                         return
171                 }
172
173         }
174
175         rValue := reflect.ValueOf(node)
176         kind = rValue.Kind()
177
178         i.getOutNode = node
179         i.getOutKind = kind
180         i.outError = nil
181 }
182
183 // Pointer to string representation function
184 func (p *JsonPointer) String() string {
185
186         if len(p.referenceTokens) == 0 {
187                 return const_empty_pointer
188         }
189
190         pointerString := const_pointer_separator + strings.Join(p.referenceTokens, const_pointer_separator)
191
192         return pointerString
193 }
194
195 // Specific JSON pointer encoding here
196 // ~0 => ~
197 // ~1 => /
198 // ... and vice versa
199
200 const (
201         const_encoded_reference_token_0 = `~0`
202         const_encoded_reference_token_1 = `~1`
203         const_decoded_reference_token_0 = `~`
204         const_decoded_reference_token_1 = `/`
205 )
206
207 func decodeReferenceToken(token string) string {
208         step1 := strings.Replace(token, const_encoded_reference_token_1, const_decoded_reference_token_1, -1)
209         step2 := strings.Replace(step1, const_encoded_reference_token_0, const_decoded_reference_token_0, -1)
210         return step2
211 }
212
213 func encodeReferenceToken(token string) string {
214         step1 := strings.Replace(token, const_decoded_reference_token_1, const_encoded_reference_token_1, -1)
215         step2 := strings.Replace(step1, const_decoded_reference_token_0, const_encoded_reference_token_0, -1)
216         return step2
217 }