1package gojsonld
2
3import (
4	"reflect"
5	"sort"
6	"strings"
7)
8
9const MAX_CONTEXT_URLS = 10
10
11var (
12	allKeywords = map[string]bool{
13		"@base":        true,
14		"@context":     true,
15		"@container":   true,
16		"@default":     true,
17		"@embed":       true,
18		"@explicit":    true,
19		"@graph":       true,
20		"@id":          true,
21		"@index":       true,
22		"@language":    true,
23		"@list":        true,
24		"@omitDefault": true,
25		"@reverse":     true,
26		"@preserve":    true,
27		"@set":         true,
28		"@type":        true,
29		"@value":       true,
30		"@vocab":       true,
31	}
32)
33
34func isKeyword(key interface{}) bool {
35	switch s := key.(type) {
36	case string:
37		return allKeywords[s]
38	}
39	return false
40}
41
42func isScalar(value interface{}) bool {
43	_, isString := value.(string)
44	_, isFloat64 := value.(float64)
45	_, isFloat32 := value.(float32)
46	_, isInt64 := value.(int64)
47	_, isInt32 := value.(int32)
48	_, isBoolean := value.(bool)
49	if isString || isFloat32 || isFloat64 || isInt32 ||
50		isInt64 || isBoolean {
51		return true
52	}
53	return false
54}
55
56func isValueObject(value interface{}) bool {
57	valueMap, isMap := value.(map[string]interface{})
58	_, containsValue := valueMap["@value"]
59	if isMap && containsValue {
60		return true
61	}
62	return false
63}
64
65func isValidValueObject(value interface{}) bool {
66	valueMap, isMap := value.(map[string]interface{})
67	if !isMap {
68		return false
69	}
70	if len(valueMap) > 4 {
71		return false
72	}
73	for key := range valueMap {
74		if key != "@value" && key != "@language" &&
75			key != "@type" && key != "@index" {
76			return false
77		}
78	}
79	_, hasLanguage := valueMap["@language"]
80	_, hasType := valueMap["@type"]
81	if hasLanguage && hasType {
82		return false
83	}
84	return true
85}
86
87func isListObject(value interface{}) bool {
88	valueMap, isMap := value.(map[string]interface{})
89	_, containsList := valueMap["@list"]
90	if isMap && containsList {
91		return true
92	}
93	return false
94}
95
96func isNil(value interface{}) bool {
97	switch value.(type) {
98	case string, int64, int32, float64, float32, bool:
99		return false
100	}
101
102	if value == nil || reflect.ValueOf(value).IsNil() {
103		return true
104	} else {
105		return false
106	}
107}
108
109func deepCompareMatters(v1, v2 interface{}, listOrderMatters bool) bool {
110	return reflect.DeepEqual(v1, v2)
111}
112
113func deepCompare(v1, v2 interface{}) bool {
114	return deepCompareMatters(v1, v2, false)
115}
116
117func deepContains(values []interface{}, value interface{}) bool {
118	for _, item := range values {
119		if deepCompare(item, value) {
120			return true
121		}
122	}
123	return false
124}
125
126func deepCopy(value interface{}) interface{} {
127	switch v := value.(type) {
128	case string, int64, int32, float64, float32:
129		valueCopy := v
130		return valueCopy
131	case []interface{}:
132		tmpArray := make([]interface{}, 0)
133		for _, item := range v {
134			tmpArray = append(tmpArray, deepCopy(item))
135		}
136		return tmpArray
137	case map[string]interface{}:
138		tmpMap := make(map[string]interface{}, 0)
139		for key, item := range v {
140			tmpMap[key] = deepCopy(item)
141		}
142		return tmpMap
143	}
144	return nil
145}
146
147func mergeValue(obj map[string]interface{}, key string, value interface{}) {
148	if obj == nil {
149		return
150	}
151
152	values, ex := obj[key].([]interface{})
153	if !ex {
154		values = make([]interface{}, 0)
155	}
156
157	if key == "@list" || isListObject(value) ||
158		!deepContains(values, value) {
159		values = append(values, value)
160		obj[key] = values
161		return
162	}
163}
164
165type InverseSlice []string
166
167func (is InverseSlice) Swap(i, j int) {
168	is[i], is[j] = is[j], is[i]
169}
170
171func (is InverseSlice) Len() int {
172	return len(is)
173}
174
175func compareShortestLeast(s1, s2 string) bool {
176	if len(s1) != len(s2) {
177		return len(s1) < len(s2)
178	} else {
179		return s1 < s2
180	}
181}
182
183func (is InverseSlice) Less(i, j int) bool {
184	s1, s2 := is[i], is[j]
185	return compareShortestLeast(s1, s2)
186}
187
188func specialSortInverse(keys []string) {
189	sort.Sort(InverseSlice(keys))
190}
191
192func sortedKeys(inputMap map[string]interface{}) []string {
193	keys := make([]string, 0)
194	for key := range inputMap {
195		keys = append(keys, key)
196	}
197	sort.Strings(keys)
198	return keys
199}
200
201func isAbsoluteIri(value string) bool {
202	// TODO: this is a bit simplistic!
203	return strings.Contains(value, ":")
204}
205
206func isIRI(value interface{}) bool {
207	//TODO improve function
208	valueString, isString := value.(string)
209	if !isString {
210		return false
211	}
212	if strings.HasPrefix(valueString, "_") {
213		return false
214	}
215	if !strings.Contains(valueString, ":") {
216		return false
217	}
218	return true
219}
220
221func isRelativeIri(value string) bool {
222	if !(isKeyword(value) || isAbsoluteIri(value)) {
223		return true
224	}
225	return false
226}
227
228func isNodeObject(value interface{}) bool {
229	valueMap, isMap := value.(map[string]interface{})
230	if !isMap {
231		return false
232	}
233	_, hasValue := valueMap["@value"]
234	_, hasList := valueMap["@list"]
235	_, hasSet := valueMap["@set"]
236	if !(hasValue || hasList || hasSet) {
237		return true
238	}
239	return false
240}
241
242func isBlankNodeIdentifier(value string) bool {
243	if strings.HasPrefix(value, "_:") {
244		return true
245	}
246	return false
247}
248
249func convertFloatValue(value string) string {
250	minusIndex := strings.Index(value, "-")
251	plusIndex := strings.Index(value, "+")
252	var index int
253	if minusIndex > plusIndex {
254		index = minusIndex
255	} else {
256		index = plusIndex
257	}
258	base := value[:(index - 1)]
259	dotIndex := strings.Index(base, ".")
260	if dotIndex < 0 {
261		base += ".0"
262	}
263	exponent := value[(index + 1):]
264	exponent = strings.TrimLeft(exponent, "0")
265	if exponent == "" {
266		exponent = "0"
267	}
268	if plusIndex > -1 {
269		return base + "E" + exponent
270	} else {
271		return base + "E-" + exponent
272	}
273}
274