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	"fmt"
31	"math"
32	"reflect"
33	"strconv"
34)
35
36func isKind(what interface{}, kind reflect.Kind) bool {
37	target := what
38	if isJsonNumber(what) {
39		// JSON Numbers are strings!
40		target = *mustBeNumber(what)
41	}
42	return reflect.ValueOf(target).Kind() == kind
43}
44
45func existsMapKey(m map[string]interface{}, k string) bool {
46	_, ok := m[k]
47	return ok
48}
49
50func isStringInSlice(s []string, what string) bool {
51	for i := range s {
52		if s[i] == what {
53			return true
54		}
55	}
56	return false
57}
58
59func marshalToJsonString(value interface{}) (*string, error) {
60
61	mBytes, err := json.Marshal(value)
62	if err != nil {
63		return nil, err
64	}
65
66	sBytes := string(mBytes)
67	return &sBytes, nil
68}
69
70func isJsonNumber(what interface{}) bool {
71
72	switch what.(type) {
73
74	case json.Number:
75		return true
76	}
77
78	return false
79}
80
81func checkJsonNumber(what interface{}) (isValidFloat64 bool, isValidInt64 bool, isValidInt32 bool) {
82
83	jsonNumber := what.(json.Number)
84
85	f64, errFloat64 := jsonNumber.Float64()
86	s64 := strconv.FormatFloat(f64, 'f', -1, 64)
87	_, errInt64 := strconv.ParseInt(s64, 10, 64)
88
89	isValidFloat64 = errFloat64 == nil
90	isValidInt64 = errInt64 == nil
91
92	_, errInt32 := strconv.ParseInt(s64, 10, 32)
93	isValidInt32 = isValidInt64 && errInt32 == nil
94
95	return
96
97}
98
99// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
100const (
101	max_json_float = float64(1<<53 - 1)  // 9007199254740991.0 	 2^53 - 1
102	min_json_float = -float64(1<<53 - 1) //-9007199254740991.0	-2^53 - 1
103)
104
105func isFloat64AnInteger(f float64) bool {
106
107	if math.IsNaN(f) || math.IsInf(f, 0) || f < min_json_float || f > max_json_float {
108		return false
109	}
110
111	return f == float64(int64(f)) || f == float64(uint64(f))
112}
113
114func mustBeInteger(what interface{}) *int {
115
116	if isJsonNumber(what) {
117
118		number := what.(json.Number)
119
120		_, _, isValidInt32 := checkJsonNumber(number)
121
122		if isValidInt32 {
123
124			int64Value, err := number.Int64()
125			if err != nil {
126				return nil
127			}
128
129			int32Value := int(int64Value)
130			return &int32Value
131
132		} else {
133			return nil
134		}
135
136	}
137
138	return nil
139}
140
141func mustBeNumber(what interface{}) *float64 {
142
143	if isJsonNumber(what) {
144
145		number := what.(json.Number)
146		float64Value, err := number.Float64()
147
148		if err == nil {
149			return &float64Value
150		} else {
151			return nil
152		}
153
154	}
155
156	return nil
157
158}
159
160// formats a number so that it is displayed as the smallest string possible
161func resultErrorFormatJsonNumber(n json.Number) string {
162
163	if int64Value, err := n.Int64(); err == nil {
164		return fmt.Sprintf("%d", int64Value)
165	}
166
167	float64Value, _ := n.Float64()
168
169	return fmt.Sprintf("%g", float64Value)
170}
171
172// formats a number so that it is displayed as the smallest string possible
173func resultErrorFormatNumber(n float64) string {
174
175	if isFloat64AnInteger(n) {
176		return fmt.Sprintf("%d", int64(n))
177	}
178
179	return fmt.Sprintf("%g", n)
180}
181
182func convertDocumentNode(val interface{}) interface{} {
183
184	if lval, ok := val.([]interface{}); ok {
185
186		res := []interface{}{}
187		for _, v := range lval {
188			res = append(res, convertDocumentNode(v))
189		}
190
191		return res
192
193	}
194
195	if mval, ok := val.(map[interface{}]interface{}); ok {
196
197		res := map[string]interface{}{}
198
199		for k, v := range mval {
200			res[k.(string)] = convertDocumentNode(v)
201		}
202
203		return res
204
205	}
206
207	return val
208}
209