1package gojsonld
2
3import (
4	"math"
5	"strconv"
6	"strings"
7)
8
9func toRDF(activeContext *Context, element interface{}) (*Dataset, error) {
10	// 1)
11	expanded, expandErr := Expand(element, activeContext.options)
12	if !isNil(expandErr) {
13		return nil, expandErr
14	}
15	// 2)
16	nodeMap := make(map[string]interface{}, 0)
17	var idGenerator = BlankNodeIdGenerator{}
18	idGenerator.counter = 0
19	idGenerator.identifierMap = make(map[string]string, 0)
20	defaultArg := "@default"
21	err := generateNodeMap(expanded, nodeMap, &defaultArg, nil,
22		nil, nil, &idGenerator)
23	if !isNil(err) {
24		return nil, err
25	}
26	// 3)
27	dataset := NewDataset()
28	// 4)
29	keys := sortedKeys(nodeMap)
30	for _, graphName := range keys {
31		graph := nodeMap[graphName]
32		// 4.1)
33		if isRelativeIri(graphName) {
34			continue
35		}
36		// 4.2)
37		triples := make([]*Triple, 0)
38		// 4.3)
39		graphMap := graph.(map[string]interface{})
40		graphKeys := sortedKeys(graphMap)
41		for _, id := range graphKeys {
42			node := graphMap[id]
43			// 4.3.1)
44			if !isAbsoluteIri(id) {
45				continue
46			}
47			nodeMapValue := node.(map[string]interface{})
48			keysNode := sortedKeys(nodeMapValue)
49			for _, property := range keysNode {
50				values := nodeMapValue[property]
51				// 4.3.2.1)
52				if property == "@type" {
53					property = RDF_TYPE
54					// 4.3.2.2)
55				} else if isKeyword(property) {
56					continue
57					// 4.3.2.3)
58				} else if strings.HasPrefix(property, "_:") &&
59					!activeContext.options.ProduceGeneralizedRdf {
60					continue
61					// 4.3.2.4)
62				} else if isRelativeIri(property) {
63					continue
64				}
65				// RDF subject
66				var subject Term
67				if strings.HasPrefix(id, "_:") {
68					subject = NewBlankNode(id[2:])
69				} else {
70					subject = NewResource(id)
71				}
72				// RDF predicate
73				var predicate Term
74				if strings.HasPrefix(property, "_:") {
75					predicate = NewBlankNode(property[2:])
76				} else {
77					predicate = NewResource(property)
78				}
79				valuesArray := values.([]interface{})
80				for _, item := range valuesArray {
81					if isListObject(item) {
82						list := item.(map[string]interface{})["@list"].([]interface{})
83						listHead, listTriples := listToRDF(list, &idGenerator)
84						triples = append(triples, NewTriple(subject, predicate,
85							listHead))
86						for _, triple := range listTriples {
87							triples = append(triples, triple)
88						}
89					} else {
90						object := objectToRDF(item)
91						if !isNil(object) {
92							triples = append(triples, NewTriple(subject, predicate,
93								object))
94						}
95					}
96				}
97			}
98		}
99		// 4.4 + 4.5)
100		if strings.HasPrefix(graphName, "_:") {
101			graphName = graphName[2:]
102		}
103		dataset.Graphs[graphName] = triples
104	}
105	return dataset, nil
106}
107
108func objectToRDF(item interface{}) Term {
109	//TODO spec is wrong
110	if itemString, isString := item.(string); isString {
111		if strings.HasPrefix(itemString, "_:") {
112			return NewBlankNode(itemString[2:])
113		} else {
114			return NewResource(itemString)
115		}
116	}
117	id, hasID := item.(map[string]interface{})["@id"]
118	language, hasLanguage := item.(map[string]interface{})["@language"]
119	// 1)
120	if isNodeObject(item) && hasID && isRelativeIri(id.(string)) {
121		return nil
122	}
123	// 2)
124	if isNodeObject(item) {
125		if strings.HasPrefix(id.(string), "_:") {
126			idString := id.(string)
127			return NewBlankNode(idString[2:])
128		} else {
129			return NewResource(id.(string))
130		}
131	}
132	// 3)
133	value := item.(map[string]interface{})["@value"]
134	// 4)
135	datatype, hasDatatype := item.(map[string]interface{})["@type"]
136	if !hasDatatype {
137		datatype = ""
138	}
139	valueBool, isBool := value.(bool)
140	valueFloat, isFloat := value.(float64)
141	valueInt, isInt := value.(int64)
142	// 5)
143	if isBool {
144		if valueBool {
145			value = "true"
146		} else {
147			value = "false"
148		}
149		if datatype == "" {
150			datatype = XSD_BOOLEAN
151		}
152		// 6)
153	} else if isFloat {
154		var tmpDatatype string
155		if valueFloat != math.Trunc(valueFloat) || datatype.(string) == XSD_DOUBLE {
156			value = strconv.FormatFloat(valueFloat, 'E', -1, 64)
157			value = convertFloatValue(value.(string))
158			tmpDatatype = XSD_DOUBLE
159		} else {
160			value = strconv.FormatFloat(valueFloat, 'f', 0, 64)
161			tmpDatatype = XSD_INTEGER
162		}
163		if datatype == "" {
164			datatype = tmpDatatype
165		}
166		// 7
167	} else if isInt || datatype.(string) == XSD_INTEGER {
168		value = strconv.FormatInt(valueInt, 10)
169		if datatype == "" {
170			datatype = XSD_INTEGER
171		}
172		// 8)
173	} else {
174		if datatype == "" {
175			if hasLanguage {
176				//TODO fix spec
177				//datatype = RDF_LANGSTRING
178				datatype = XSD_STRING
179			} else {
180				datatype = XSD_STRING
181			}
182		}
183	}
184	datatypeTerm := NewResource(datatype.(string))
185	if hasLanguage {
186		return NewLiteralWithLanguageAndDatatype(value.(string), language.(string),
187			datatypeTerm.(Term))
188	}
189	return NewLiteralWithDatatype(value.(string), datatypeTerm.(Term))
190}
191
192func listToRDF(list []interface{}, idGenerator *BlankNodeIdGenerator) (Term, []*Triple) {
193	listTriples := make([]*Triple, 0)
194	// 1)
195	if len(list) == 0 {
196		return NewResource(RDF_NIL), listTriples
197	}
198	// 2)
199	bnodes := make([]Term, 0)
200	for i := 0; i < len(list); i++ {
201		id := idGenerator.generateBlankNodeIdentifier(nil)
202		bnodes = append(bnodes,
203			NewBlankNode(id[2:]))
204	}
205	// 3)
206	listTriples = make([]*Triple, 0)
207	// 4)
208	for i := 0; i < len(list); i++ {
209		subject := bnodes[i]
210		item := list[i]
211		// 4.1)
212		object := objectToRDF(item)
213		// 4.2)
214		if !isNil(object) {
215			listTriples = append(listTriples,
216				NewTriple(subject, NewResource(RDF_FIRST), object))
217		}
218		// 4.3)
219		var rest Term
220		if i == len(list)-1 {
221			rest = NewResource(RDF_NIL)
222		} else {
223			rest = bnodes[i+1]
224		}
225		listTriples = append(listTriples,
226			NewTriple(subject, NewResource(RDF_REST), rest))
227	}
228	// 5)
229	return bnodes[0], listTriples
230}
231
232func fromRDF(dataset *Dataset, useNativeTypes bool,
233	useRdfType bool) []interface{} {
234	// 1)
235	defaultGraph := make(map[string]interface{}, 0)
236	// 2)
237	graphMap := make(map[string]interface{})
238	graphMap["@default"] = defaultGraph
239	//TODO possible draft error
240	nodeUsagesMap := make(map[string]interface{}, 0)
241	// 3)
242	for name, graph := range dataset.Graphs {
243		// 3.2)
244		if _, hasGraph := graphMap[name]; !hasGraph {
245			graphMap[name] = make(map[string]interface{}, 0)
246		}
247		// 3.3)
248		_, hasName := defaultGraph[name]
249		if name != "@default" && !hasName {
250			tmpMap := make(map[string]interface{}, 0)
251			tmpMap["@id"] = name
252			defaultGraph[name] = tmpMap
253
254		}
255		// 3.4)
256		nodeMap := graphMap[name].(map[string]interface{})
257		// 3.5)
258		for _, triple := range graph {
259			subject := triple.Subject.RawValue()
260			predicate := triple.Predicate.RawValue()
261			object := triple.Object.RawValue()
262			// 3.5.1)
263			if _, hasSubject := nodeMap[subject]; !hasSubject {
264				tmpMap := make(map[string]interface{}, 0)
265				tmpMap["@id"] = subject
266				nodeMap[subject] = tmpMap
267			}
268			// 3.5.2)
269			node := nodeMap[subject].(map[string]interface{})
270			// 3.5.3)
271			_, hasObject := nodeMap[object]
272			if (isBlankNodeIdentifier(object) || isIRI(object)) && !hasObject {
273				tmpMap := make(map[string]interface{}, 0)
274				tmpMap["@id"] = object
275				nodeMap[object] = tmpMap
276			}
277			// 3.5.4)
278			if predicate == RDF_TYPE && !useRdfType &&
279				(isBlankNodeIdentifier(object) || isIRI(object)) {
280				mergeValue(node, "@type", object)
281				continue
282			}
283			// 3.5.5)
284			value := rdfToObject(triple.Object, useNativeTypes)
285			// 3.5.6+7)
286			mergeValue(node, predicate, value)
287			// 3.5.8)
288			if isIRI(object) || isBlankNodeIdentifier(object) {
289				nodeObjectMap := nodeMap[object].(map[string]interface{})
290				tmpMap := make(map[string]interface{}, 0)
291				tmpMap["node"] = node
292				tmpMap["property"] = predicate
293				tmpMap["value"] = value
294				mergeValue(nodeObjectMap, "usages", tmpMap)
295				nodeMap[object] = nodeObjectMap
296				//TODO spec is wrong
297				mergeValue(nodeUsagesMap, object, name+" "+node["@id"].(string))
298			}
299		}
300	}
301	// 4)
302	for name := range graphMap {
303		graphObject := graphMap[name].(map[string]interface{})
304		// 4.1)
305		if _, hasNil := graphObject[RDF_NIL]; !hasNil {
306			continue
307		}
308		// 4.2)
309		//Spec defines this variable's name as nil, but nil is a reserved
310		//keyword in go so I named it as nilValue instead
311		nilValue := graphObject[RDF_NIL].(map[string]interface{})
312		// 4.3)
313		usages := nilValue["usages"].([]interface{})
314		for index := range usages {
315			usage := usages[index].(map[string]interface{})
316			// 4.3.1)
317			node := usage["node"].(map[string]interface{})
318			property := usage["property"].(string)
319			head := usage["value"].(map[string]interface{})
320			// 4.3.2)
321			list := make([]interface{}, 0)
322			//TODO check type of listNodes
323			listNodes := make([]interface{}, 0)
324			// 4.3.3)
325			for RDF_REST == property && isWellFormedListNode(node) &&
326				len(nodeUsagesMap[node["@id"].(string)].([]interface{})) == 1 {
327				// 4.3.3.1)
328				list = append(list, node[RDF_FIRST].([]interface{})[0])
329				// 4.3.3.2)
330				listNodes = append(listNodes, node["@id"])
331				// 4.3.3.3)
332				nodeUsage := node["usages"].([]interface{})[0].(map[string]interface{})
333				// 4.3.3.4)
334				node = nodeUsage["node"].(map[string]interface{})
335				property = nodeUsage["property"].(string)
336				head = nodeUsage["value"].(map[string]interface{})
337				// 4.3.3.5)
338				if !isBlankNodeIdentifier(node["@id"].(string)) {
339					break
340				}
341			}
342			// 4.3.4)
343			if property == RDF_FIRST {
344				// 4.3.4.1)
345				if RDF_NIL == node["@id"].(string) {
346					continue
347				}
348				//4.3.4.3)
349				headID := head["@id"].(string)
350				// 4.3.4.4)
351				head = graphObject[headID].(map[string]interface{})
352				// 4.3.4.5)
353				head = head[RDF_REST].([]interface{})[0].(map[string]interface{})
354				// 4.3.4.6)
355				list = list[:(len(list) - 1)]
356				listNodes = listNodes[:(len(listNodes) - 1)]
357			}
358			// 4.3.5)
359			delete(head, "@id")
360			// 4.3.6)
361			for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
362				list[i], list[j] = list[j], list[i]
363			}
364			// 4.3.7)
365			head["@list"] = list
366			// 4.3.8)
367			for _, nodeID := range listNodes {
368				delete(graphObject, nodeID.(string))
369			}
370		}
371
372	}
373	// 5)
374	result := make([]interface{}, 0)
375	// 6)
376	keys := sortedKeys(defaultGraph)
377	for _, subject := range keys {
378		node := defaultGraph[subject].(map[string]interface{})
379		// 6.1)
380		if _, hasSubject := graphMap[subject]; hasSubject {
381			// 6.1.1)
382			node["@graph"] = make([]interface{}, 0)
383			// 6.1.2)
384			keysGraph := sortedKeys(graphMap[subject].(map[string]interface{}))
385			for _, s := range keysGraph {
386				n := graphMap[subject].(map[string]interface{})[s]
387				nMap := n.(map[string]interface{})
388				_, hasID := nMap["@id"]
389				delete(nMap, "usages")
390				if len(nMap) == 1 && hasID {
391					continue
392				}
393				node["@graph"] = append(node["@graph"].([]interface{}), nMap)
394			}
395		}
396		// 6.2)
397		delete(node, "usages")
398		if _, hasID := node["@id"]; !(len(node) == 1 && hasID) {
399			result = append(result, node)
400		}
401	}
402	// 7)
403	return result
404}
405
406func isWellFormedListNode(node interface{}) bool {
407	nodeMap := node.(map[string]interface{})
408	//TODO spec has no mention of @id
409	for key := range nodeMap {
410		if key != "@id" && key != "@type" && key != RDF_FIRST &&
411			key != RDF_REST && key != "usages" {
412			return false
413		}
414	}
415	if !isBlankNodeIdentifier(nodeMap["@id"].(string)) {
416		return false
417	}
418	if usages, hasUsages := nodeMap["usages"]; hasUsages {
419		usagesArray, isArray := usages.([]interface{})
420		if !(isArray && len(usagesArray) == 1) {
421			return false
422		}
423	} else {
424		return false
425	}
426	if first, hasKey := nodeMap[RDF_FIRST]; hasKey {
427		firstArray, isArray := first.([]interface{})
428		if !(isArray && len(firstArray) == 1) {
429			return false
430		}
431	} else {
432		return false
433	}
434	if rest, hasKey := nodeMap[RDF_REST]; hasKey {
435		restArray, isArray := rest.([]interface{})
436		if !(isArray && len(restArray) == 1) {
437			return false
438		}
439	} else {
440		return false
441	}
442	if typeValue, hasType := nodeMap["@type"]; hasType {
443		typeArray, isArray := typeValue.([]interface{})
444		if !(isArray && len(typeArray) == 1 && RDF_LIST == typeArray[0]) {
445			return false
446		}
447	}
448	return true
449}
450
451func rdfToObject(value Term, useNativeTypes bool) map[string]interface{} {
452	// 1)
453	if isTermResource(value) || isTermBlankNode(value) {
454		returnValue := make(map[string]interface{}, 0)
455		returnValue["@id"] = value.RawValue()
456		return returnValue
457	}
458	//2)
459	valueLiteral := value.(*Literal)
460	// 2.1)
461	result := make(map[string]interface{}, 0)
462	// 2.2)
463	var convertedValue interface{}
464	convertedValue = valueLiteral.Value
465	// 2.3)
466	var typeValue interface{}
467	typeValue = nil
468	// 2.4)
469	if useNativeTypes {
470		// 2.4.1)
471		if valueLiteral.Datatype.RawValue() == XSD_STRING {
472			//TODO java version is different and does not add
473			//string to result in this case
474			//does nothing
475			// 2.4.2)
476		} else if valueLiteral.Datatype.RawValue() == XSD_BOOLEAN {
477			if convertedValue == "true" {
478				convertedValue = true
479			} else if convertedValue == "false" {
480				convertedValue = false
481			}
482			// 2.4.3)
483		} else if valueLiteral.Datatype.RawValue() == XSD_DOUBLE ||
484			valueLiteral.Datatype.RawValue() == XSD_INTEGER {
485			floatValue, floatErr := strconv.ParseFloat(convertedValue.(string), 64)
486			if isNil(floatErr) {
487				convertedValue = floatValue
488			}
489		} else {
490			typeValue = valueLiteral.Datatype.RawValue()
491		}
492		// 2.5)
493	} else if valueLiteral.Language != "" {
494		result["@language"] = valueLiteral.Language
495		// 2.6)
496	} else {
497		if valueLiteral.Datatype.RawValue() != XSD_STRING {
498			typeValue = valueLiteral.Datatype.RawValue()
499		}
500	}
501	// 2.7)
502	result["@value"] = convertedValue
503	// 2.8)
504	if !isNil(typeValue) {
505		result["@type"] = typeValue
506	}
507	// 2.9)
508	return result
509}
510