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  gojsonschema
20// repository-desc  An implementation of JSON Schema, based on IETF's draft v4 - Go language.
21//
22// description      Various utility functions.
23//
24// created          26-02-2013
25
26package gojsonschema
27
28import (
29	"encoding/json"
30	"math/big"
31	"reflect"
32)
33
34func isKind(what interface{}, kinds ...reflect.Kind) bool {
35	target := what
36	if isJSONNumber(what) {
37		// JSON Numbers are strings!
38		target = *mustBeNumber(what)
39	}
40	targetKind := reflect.ValueOf(target).Kind()
41	for _, kind := range kinds {
42		if targetKind == kind {
43			return true
44		}
45	}
46	return false
47}
48
49func existsMapKey(m map[string]interface{}, k string) bool {
50	_, ok := m[k]
51	return ok
52}
53
54func isStringInSlice(s []string, what string) bool {
55	for i := range s {
56		if s[i] == what {
57			return true
58		}
59	}
60	return false
61}
62
63// indexStringInSlice returns the index of the first instance of 'what' in s or -1 if it is not found in s.
64func indexStringInSlice(s []string, what string) int {
65	for i := range s {
66		if s[i] == what {
67			return i
68		}
69	}
70	return -1
71}
72
73func marshalToJSONString(value interface{}) (*string, error) {
74
75	mBytes, err := json.Marshal(value)
76	if err != nil {
77		return nil, err
78	}
79
80	sBytes := string(mBytes)
81	return &sBytes, nil
82}
83
84func marshalWithoutNumber(value interface{}) (*string, error) {
85
86	// The JSON is decoded using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
87	// This means the numbers are internally still represented as strings and therefore 1.00 is unequal to 1
88	// One way to eliminate these differences is to decode and encode the JSON one more time without Decoder.UseNumber
89	// so that these differences in representation are removed
90
91	jsonString, err := marshalToJSONString(value)
92	if err != nil {
93		return nil, err
94	}
95
96	var document interface{}
97
98	err = json.Unmarshal([]byte(*jsonString), &document)
99	if err != nil {
100		return nil, err
101	}
102
103	return marshalToJSONString(document)
104}
105
106func isJSONNumber(what interface{}) bool {
107
108	switch what.(type) {
109
110	case json.Number:
111		return true
112	}
113
114	return false
115}
116
117func checkJSONInteger(what interface{}) (isInt bool) {
118
119	jsonNumber := what.(json.Number)
120
121	bigFloat, isValidNumber := new(big.Rat).SetString(string(jsonNumber))
122
123	return isValidNumber && bigFloat.IsInt()
124
125}
126
127// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
128const (
129	maxJSONFloat = float64(1<<53 - 1)  // 9007199254740991.0 	 2^53 - 1
130	minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0	-2^53 - 1
131)
132
133func mustBeInteger(what interface{}) *int {
134
135	if isJSONNumber(what) {
136
137		number := what.(json.Number)
138
139		isInt := checkJSONInteger(number)
140
141		if isInt {
142
143			int64Value, err := number.Int64()
144			if err != nil {
145				return nil
146			}
147
148			int32Value := int(int64Value)
149			return &int32Value
150		}
151
152	}
153
154	return nil
155}
156
157func mustBeNumber(what interface{}) *big.Rat {
158
159	if isJSONNumber(what) {
160		number := what.(json.Number)
161		float64Value, success := new(big.Rat).SetString(string(number))
162		if success {
163			return float64Value
164		}
165	}
166
167	return nil
168
169}
170
171func convertDocumentNode(val interface{}) interface{} {
172
173	if lval, ok := val.([]interface{}); ok {
174
175		res := []interface{}{}
176		for _, v := range lval {
177			res = append(res, convertDocumentNode(v))
178		}
179
180		return res
181
182	}
183
184	if mval, ok := val.(map[interface{}]interface{}); ok {
185
186		res := map[string]interface{}{}
187
188		for k, v := range mval {
189			res[k.(string)] = convertDocumentNode(v)
190		}
191
192		return res
193
194	}
195
196	return val
197}
198