Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/encoding/rdf-xml.lisp
| Kind | Covered | All | % |
| expression | 110 | 805 | 13.7 |
| branch | 3 | 54 | 5.6 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.implementation; -*-
3
(in-package :org.datagraph.spocq.implementation)
6
;;; http://www.w3.org/TR/rdf-sparql-XMLres/
10
(defparameter *rdf-iri-cache* nil)
11
(defparameter *namespace-prefix-count* 0)
14
;;; solution field writing - when as rdf, require just s-p-o
16
(defgeneric write-rdf+xml (results stream)
17
(:documentation "Encode the result field to the stream as rdf+xml.
18
If results are a list of lists, then the first element is a header, which specifies the variable names and
19
the remaining entries aer solutions sets. if a variable is unbound, the solution element is etf:nil.
20
Otherwise, the results can be a matrix, in which case the dimensionality is explicit.")
22
(:method ((result symbol) (stream t))
23
(spocq.e:request-error "RDF+XML result is not a graph: ~s." result)
26
(:method ((results list-solution-field) (stream t))
27
(write-rdf+xml (cons (list-solution-field-dimensions results)
28
(list-solution-field-solutions results))
31
(:method ((results cons) (stream t))
32
(let* ((dimensions (first results))
33
(solutions (rest results))
35
(start (or (response-offset) 0))
37
(*rdf-iri-cache* (make-hash-table :test 'equal))
38
(*namespace-prefix-count* 0)
39
(previous-subject nil))
41
(cond ((equal dimensions *construct-dimensions*)
43
((= (length dimensions) (length *construct-dimensions*))
44
;; (log-warn "RDF+XML dimensions assumed: dimensions ~s." dimensions)
45
;;(setf dimensions *construct-dimensions*)
48
(spocq.e:request-error "RDF+XML result is not a graph: dimensions ~s." dimensions)
49
(return-from write-rdf+xml 0)))
52
"<?xml version='1.0' encoding='utf-8'?>
54
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
55
xmlns:rdfs='http://www.w3.org/2000/01/rdf-schema#'
56
xmlns:xsd='http://www.w3.org/2001/XMLSchema#'>
58
(dolist (solution solutions)
59
(when (>= index start)
60
(when (and end (>= index end))
62
(destructuring-bind (subject predicate object) solution
64
(cond ((equalp previous-subject subject)
65
(format stream "~% "))
66
(t (unless (= index start)
67
(format stream "</rdf:Description>"))
68
(encode-rdf-xml-description-subject subject stream)))
69
(encode-rdf-xml-description-predicate-and-object predicate object stream)
70
(setf previous-subject subject)
72
(unless (= index start)
73
(format stream "</rdf:Description>"))
74
(format stream "</rdf:RDF>~%")
75
(incf-stat *statements-returned* index)))
77
(:method ((results boolean-generator) (stream t))
78
(spocq.e:request-error "attempt to encode a boolean result as rdf+xml")
81
(:method ((results solution-generator) (stream t))
82
(let* ((dimensions (solution-generator-dimensions results))
83
(channel (solution-generator-channel results))
84
(variable-count (length dimensions))
86
(start (or (response-offset) 0))
88
(*rdf-iri-cache* (make-hash-table :test 'equal))
89
(*namespace-prefix-count* 0))
91
(cond ((equal dimensions *construct-dimensions*)
93
((and (= (length dimensions) 4)
94
(equal (subseq dimensions 0 3) *construct-dimensions*))
96
((= (length dimensions) (length *construct-dimensions*))
97
(log-warn "RDF+XML dimensions assumed: dimensions ~s." dimensions)
98
(setf dimensions *construct-dimensions*))
100
(spocq.e:request-error "RDF+XML result is not a graph: dimensions ~s." dimensions)
101
(return-from write-rdf+xml 0)))
104
"<?xml version='1.0' encoding='utf-8'?>
106
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
107
xmlns:rdfs='http://www.w3.org/2000/01/rdf-schema#'
108
xmlns:xsd='http://www.w3.org/2001/XMLSchema#'>
110
(do-pages (page channel)
111
(if (and end (>= index end))
113
(if (>= (+ index (array-dimension page 0)) start)
114
(cond ((= variable-count (array-dimension page 1))
115
(write-sparql-results-field-rdf+xml page stream index start end))
117
(log-warn "field width mismatch: ~s : ~s."
118
dimensions (array-dimension page 1))
119
(incf index (array-dimension page 0))))
120
; otherwise skip the entire page
121
(incf index (array-dimension page 0)))))
122
(format stream "</rdf:RDF>~%")
123
(incf-stat *statements-returned* index)))
125
(:method ((result-field true-matrix-field) (stream t))
126
(spocq.e:request-error "attempt to encode a boolean result as rdf+xml")
129
(:method ((result-field false-matrix-field) (stream t))
130
(spocq.e:request-error "attempt to encode a boolean result as rdf+xml")
133
(:method ((result-field matrix-field) (stream t))
134
(let ((term-deconstructor (repository-term-deconstructor *transaction*))
135
(dimensions (solution-field-dimensions result-field))
137
(start (or (response-offset) 0))
140
(cond ((equal dimensions *construct-dimensions*)
142
((and (= (length dimensions) 4)
143
(equal (subseq dimensions 0 3) *construct-dimensions*))
146
(spocq.e:request-error "RDF+XML result is not a graph: dimensions ~s." dimensions)
147
(return-from write-rdf+xml 0)))
149
(with-input-fields (result-field)
150
(let ((%source-data (cffi::null-pointer))
152
(setf (values %source-data source-row) (first-field-row result-field))
153
(loop until (and end (>= result-count (the fixnum end)))
154
until (cffi:null-pointer-p %source-data)
156
(trace-matrix "~& write-rdf+xml.next ~@{~a ~}" :source-row source-row)
157
(when (> (incf result-count) start)
158
(let* ((term-offset (* base-width source-row))
159
(subject-term-id (#.+matrix-accessor+ (the sb-sys:system-area-pointer %source-data)
160
(the fixnum (* #.(cffi:foreign-type-size +matrix-element-type+) term-offset))))
161
(predicate-term-id (#.+matrix-accessor+ (the sb-sys:system-area-pointer %source-data)
162
(the fixnum (* #.(cffi:foreign-type-size +matrix-element-type+) (+ term-offset 1)))))
163
(object-term-id (#.+matrix-accessor+ (the sb-sys:system-area-pointer %source-data)
164
(the fixnum (* #.(cffi:foreign-type-size +matrix-element-type+) (+ term-offset 2))))))
165
(unless (or (= subject-term-id +null-term-id+)
166
(= predicate-term-id +null-term-id+)
167
(= object-term-id +null-term-id+))
168
(flet ((subject-term-aspect-encoder (term-type term-literal term-language-tag term-datatype)
169
;; encode the element start tag as either an about iri or a node id.
170
(encode-rdf-xml-description-subject-aspects term-type term-literal term-language-tag term-datatype stream))
171
(predicate-term-aspect-encoder (term-type term-literal term-language-tag term-datatype)
172
;; encode the predicated relation as either an iri or a node and
173
;; complete the element with the content
174
(flet ((object-term-aspect-encoder (o-term-type o-term-literal o-term-language-tag o-term-datatype)
175
(encode-rdf-xml-description-predicate-and-object-aspects term-type term-literal term-language-tag term-datatype
176
o-term-type o-term-literal o-term-language-tag o-term-datatype
178
(declare (dynamic-extent #'object-term-aspect-encoder))
179
(funcall term-deconstructor #'object-term-aspect-encoder *transaction* object-term-id))))
180
(declare (dynamic-extent #'subject-term-aspect-encoder))
181
(declare (dynamic-extent #'predicate-term-aspect-encoder))
182
(write-string " " stream)
183
(funcall term-deconstructor #'subject-term-aspect-encoder *transaction* subject-term-id)
184
(funcall term-deconstructor #'predicate-term-aspect-encoder *transaction* predicate-term-id)
185
(format stream "</rdf:Description>~%"))))
186
(setf (values %source-data source-row) (next-field-row result-field)))))
187
(incf-stat *statements-returned* (- result-count start)))))))
190
(defgeneric rdf+xml-iri-parts (iri)
191
(:method ((iri spocq:iri))
192
(rdf+xml-iri-parts (spocq:iri-lexical-form iri)))
193
(:method ((iri puri:uri))
194
(rdf+xml-iri-parts (iri-lexical-form iri)))
195
(:method ((iri symbol))
196
(rdf+xml-iri-parts (iri-lexical-form iri)))
197
(:method ((iri string))
198
(flet ((split-iri (literal)
199
(let ((scheme (parse-uri-scheme literal)))
202
(let ((split-position (position-if #'(lambda (c) (or (eql c #\#) (eql c #\/))) literal :from-end t)))
203
(unless split-position
204
(spocq.e:request-error "Invalid rdf iri: ~s" literal))
205
(incf split-position)
206
(values (subseq literal 0 split-position)
207
(subseq literal split-position))))
208
((:|URN:UUID| :urn :uuid)
209
(let ((split-position (position #\: literal :from-end t)))
210
(unless split-position
211
(spocq.e:request-error "Invalid rdf iri: ~s" literal))
212
(incf split-position)
213
(values (subseq literal 0 split-position)
214
(subseq literal split-position))))
216
(spocq.e:request-error "Invalid rdf iri: ~s" literal))
218
(let ((split-position (position-if #'(lambda (c) (or (eql c #\#) (eql c #\/))) literal :from-end nil)))
219
(unless split-position
220
(spocq.e:request-error "Invalid rdf iri: ~s" literal))
221
(incf split-position)
222
(values (subseq literal 0 split-position)
223
(subseq literal split-position)))))))
224
(namespace-name-prefix (namespace-name)
225
(or (gethash namespace-name *rdf-iri-cache*)
226
(setf (gethash namespace-name *rdf-iri-cache*)
227
(format nil "ns~d" (incf *namespace-prefix-count*))))))
228
(let ((cached-aspects (gethash iri *rdf-iri-cache*)))
230
(apply #'values cached-aspects)
231
(multiple-value-bind (namespace-name local-part) (split-iri iri)
232
(let* ((prefix (namespace-name-prefix namespace-name))
233
(aspects (list prefix namespace-name local-part)))
234
(setf (gethash iri *rdf-iri-cache*) aspects)
235
(values prefix namespace-name local-part))))))))
236
;;; (let ((*rdf-iri-cache* (make-hash-table :test 'equal))) (rdf+xml-iri-parts "urn:dydra:accessToken"))
237
;;; (let ((*rdf-iri-cache* (make-hash-table :test 'equal))) (rdf+xml-iri-parts "uuid:00000000-1111-2222-3333-444444444444"))
240
(defun encode-rdf-xml-description-subject-aspects (term-type term-literal term-language-tag term-datatype stream)
241
"Encode the description element start tag with an about or a node id attribute respective
242
the subject term type"
243
(declare (ignore term-language-tag term-datatype))
245
(write-string "<rdf:Description " stream)
247
(:node (format stream "rdf:nodeID='~@[~a_~]" (blank-node-prefix))
248
(stream-write-external-utf8-string-as-xml stream term-literal)
249
(write-char #\' stream))
250
(:uri (format stream "rdf:about='")
251
(stream-write-external-utf8-string-as-xml stream term-literal)
252
(write-char #\' stream))
253
(t (spocq.e:request-error "Invalid subject term type: ~s" term-type)))
254
(write-char #\> stream))
257
(defun encode-rdf-xml-description-predicate-and-object-aspects (p-term-type p-term-literal p-term-language-tag p-term-datatype
258
o-term-type o-term-literal o-term-language-tag o-term-datatype
260
"Encode the predicate/object element with a generic identifier and namespace binding, with
261
attributes respective the object datatype and with the object term literal as content."
262
(declare (ignore p-term-language-tag p-term-datatype))
266
(t (spocq.e:request-error "Invalid predicate term type: ~s" p-term-type)))
267
(with-term-string (p-term-literal-string p-term-literal) ; do it here in order to avoid 1.0.46 compiler bug
268
(multiple-value-bind (prefix namespace-name local-part)
269
(rdf+xml-iri-parts p-term-literal-string)
271
(format stream "<~a:~a xmlns:~a='~a' "
272
prefix local-part prefix namespace-name)
276
(:node ; encode a blank node
277
(format stream "rdf:nodeID='~@[~a_~]" (blank-node-prefix))
278
(stream-write-external-utf8-string-as-xml stream o-term-literal)
279
(write-string "'/>" stream))
280
(:literal ; encode a typed or language-tagged literal
281
; in the latter case, suppress any datatype
282
(cond ((not (cffi:null-pointer-p o-term-language-tag))
283
(write-string "xml:lang='" stream)
284
(stream-write-external-utf8-string-as-xml stream o-term-language-tag)
285
(write-string "' " stream))
286
((and (not (cffi:null-pointer-p o-term-datatype))
287
(not (%string-equal o-term-datatype (|%http://www.w3.org/2001/XMLSchema#string|))))
288
(write-string "rdf:datatype='" stream)
289
(stream-write-external-utf8-string-as-xml stream o-term-datatype)
290
(write-string "' " stream)))
291
(write-char #\> stream)
292
(stream-write-external-utf8-string-as-xml stream o-term-literal)
293
(format stream "</~a:~a>" prefix local-part))
295
(write-string "rdf:resource='" stream)
296
(stream-write-external-utf8-string-as-xml stream o-term-literal)
297
(write-string "'/>" stream))))))
301
(defun write-sparql-results-field-rdf+xml (page stream &optional (index 0) (start 0) end)
302
(let ((term-deconstructor (repository-term-deconstructor *transaction*)))
303
(dotimes (page-index (array-dimension page 0))
304
(when (>= index start)
305
(when (and end (>= index end))
307
(let ((subject-term-id (aref page page-index 0))
308
(predicate-term-id (aref page page-index 1))
309
(object-term-id (aref page page-index 2)))
310
(unless (or (= subject-term-id +null-term-id+)
311
(= predicate-term-id +null-term-id+)
312
(= object-term-id +null-term-id+))
313
(flet ((subject-term-aspect-encoder (term-type term-literal term-language-tag term-datatype)
314
;; encode the element start tag as either an about iri or a node id.
315
(encode-rdf-xml-description-subject-aspects term-type term-literal term-language-tag term-datatype stream))
316
(predicate-term-aspect-encoder (term-type term-literal term-language-tag term-datatype)
317
;; encode the predicated relation as either an iri or a node and
318
;; complete the element with the content
319
(flet ((object-term-aspect-encoder (o-term-type o-term-literal o-term-language-tag o-term-datatype)
320
(encode-rdf-xml-description-predicate-and-object-aspects term-type term-literal term-language-tag term-datatype
321
o-term-type o-term-literal o-term-language-tag o-term-datatype
323
(declare (dynamic-extent #'object-term-aspect-encoder))
324
(funcall term-deconstructor #'object-term-aspect-encoder *transaction* object-term-id))))
325
(declare (dynamic-extent #'subject-term-aspect-encoder))
326
(declare (dynamic-extent #'predicate-term-aspect-encoder))
327
(write-string " " stream)
328
(funcall term-deconstructor #'subject-term-aspect-encoder *transaction* subject-term-id)
329
(funcall term-deconstructor #'predicate-term-aspect-encoder *transaction* predicate-term-id)
330
(format stream "</rdf:Description>~%")))
338
(defgeneric encode-rdf-xml-description-subject (subject-term stream)
340
"Encode the complete description element start tag with an about or a node id attribute respective
341
the subject term type")
343
(:method :before ((term t) stream)
344
(format stream "~% <rdf:Description "))
346
(:method :after ((term t) stream)
347
(write-char #\> stream))
349
(:method ((object spocq:blank-node) stream)
350
(format stream "rdf:nodeID='~@[~a_~]~a'" (blank-node-prefix) (spocq:blank-node-label object)))
352
(:method ((object spocq:iri) stream)
353
(format stream "rdf:about='~/format-xml-iri-namestring/'" (spocq:iri-lexical-form object)))
355
(:method ((object symbol) stream)
356
(let ((uri-namestring (symbol-uri-namestring object)))
357
(cond (uri-namestring
358
(format stream "rdf:about='~/format-xml-iri-namestring/'" uri-namestring))
360
(error "RDF encoding error: invalid value: ~s." object))))))
363
(defun encode-rdf-xml-description-predicate-and-object (predicate object stream)
364
"Encode the predicate as the rdf element start with a generic identifier and namespace binding.
365
Leave the start tag incomplete in order that the object can be encoded as additional attributes
367
Delegate the object envoding to encode-rdf-xml-object.
368
Finally, close the predicate element."
370
(multiple-value-bind (prefix namespace-name local-part)
371
(rdf+xml-iri-parts predicate)
372
(format stream "<~a:~a xmlns:~a='~a' " ;; leave it open for the object to complete
373
prefix local-part prefix namespace-name)
374
(encode-rdf-xml-object object stream)
375
(format stream "</~a:~a>" prefix local-part)))
378
(defgeneric encode-rdf-xml-object (term stream)
379
(:method ((object function) stream)
380
(funcall object stream))
382
(:method ((object null) (stream t))
383
;; just in case, leave the syntax intact
384
(write-char #\> stream))
386
(:method ((object spocq:blank-node) stream)
387
(format stream "rdf:nodeID='~@[~a_~]~a'>" (blank-node-prefix) (spocq:blank-node-label object)))
389
(:method ((object spocq:iri) stream)
390
(format stream "><rdf:Description rdf:about='~/format-xml-iri-namestring/'/>"
391
(spocq:iri-lexical-form object)))
393
(:method ((object symbol) stream)
395
((spocq.a:|true| spocq.a:|false|)
396
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#boolean'>~a"
397
(symbol-name object)))
399
(let ((uri-namestring (symbol-uri-namestring object)))
400
(format stream "><rdf:Description rdf:about='~/format-xml-iri-namestring/'/>"
403
(:method ((object spocq:boolean) stream)
404
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#boolean'>~a"
405
(spocq:literal-lexical-form object)))
407
(:method ((object spocq:date) stream)
408
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#date'>~a"
409
(term-lexical-form object)))
411
(:method ((object spocq:date-time) stream)
412
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#dateTime'>~a"
413
(term-lexical-form object)))
415
(:method ((object spocq:time) stream)
416
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#time'>~a"
417
(term-lexical-form object)))
419
(:method ((object spocq:day-time-duration) stream)
420
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#dayTimeDuration'>~a"
421
(term-lexical-form object)))
423
(:method ((object spocq:year-month-duration) stream)
424
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#yearMonthDuration'>~a"
425
(term-lexical-form object)))
427
(:method ((object spocq:plain-literal) stream)
428
(format stream "xml:lang='~a'>~/format-xml-character-data/"
429
(spocq:plain-literal-language-tag object)
430
(spocq:literal-lexical-form object)))
432
(:method ((object string) stream)
433
(format stream ">~/format-xml-character-data/"
436
(:method ((object spocq:unbound-variable) stream)
437
;; just in case, leave the syntax intact
438
(write-char #\> stream))
440
(:method ((object spocq:atomic-typed-literal) stream)
441
(format stream "rdf:datatype='~/format-xml-iri-namestring/'>~/format-xml-character-data/"
442
(spocq:literal-datatype-uri object)
443
(spocq:literal-lexical-form object)))
445
(:method ((object spocq:unsupported-typed-literal) stream)
446
(format stream "rdf:datatype='~/format-xml-iri-namestring/'>~/format-xml-character-data/"
447
(spocq:unsupported-typed-literal-datatype-uri object)
448
(spocq:literal-lexical-form object)))
450
(:method ((object integer) stream)
451
(format stream "rdf:datatype='~a'>~a"
452
(if *encode-object-subtypes*
454
((signed-byte 16) "http://www.w3.org/2001/XMLSchema#short")
455
(t "http://www.w3.org/2001/XMLSchema#integer"))
456
"http://www.w3.org/2001/XMLSchema#integer")
459
(:method ((object double-float) stream)
460
(if (or (eql object dsu.codecs:double-float-nan)
461
(eql object dsu.codecs:double-float-positive-infinity)
462
(eql object dsu.codecs:double-float-negative-infinity))
463
(spocq.e:request-error "encoding error: invalid float value: ~a" object)
464
(let ((*read-default-float-format* 'double-float))
465
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#double'>~f" object))))
467
(:method ((object single-float) stream)
468
(if (or (eql object nan) (eql object +inf) (eql object -inf))
469
(spocq.e:request-error "Invalid float value: ~a" object)
470
(let ((*read-default-float-format* 'single-float))
471
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#float'>~f" object))))
473
(:method ((object rational) stream)
474
(let ((*read-default-float-format* 'single-float))
475
(format stream "rdf:datatype='http://www.w3.org/2001/XMLSchema#decimal'>~f"
476
(float object 1.0s0)))))
479
(:documentation (send-error-message send-response-message)
480
"The content-type application/rdf+xml indicates a response message message is to be coded
481
as xml. This applies to responses only and corresponds to sparql requests.")
483
(defmethod send-response-message (operation (message-body t) (stream t) (content-type mime:application/rdf+xml))
484
"Given a MESSAGE, and a STREAM with the application/rdf+xml CONTENT-TYPE, encode as an xml rdf document"
485
(when *encoding-trace-output*
486
(setf stream (make-broadcast-stream *encoding-trace-output* stream)))
487
(let ((*package* *spocq-reader-package*))
488
(write-rdf+xml message-body stream)))
492
(defparameter *rdf+xml=test=field*
493
(cons *construct-dimensions*
494
`( (<http://example/1> <http://example/p1> "object string")
495
(<http://example/1> <http://example/p2> <http://example/object-uri>)
496
(<http://example/1> <http://example/p2> |rdf|:|type|)
497
(<http://example/1> <http://example/p2> ,(spocq:make-plain-literal "plain string" "EN") )
498
(<http://example/2> <http://example/numeric> 3)
499
(<http://example/2> <http://example/numeric> 3.1s0)
500
(<http://example/2> <http://example/numeric> 3.2d0)
501
(<http://example/2> <http://example/numeric> 10/3)
502
(<http://example/3> <http://example/temporal> ,(spocq:make-date :year 2001 :month 01 :day 02))
503
(<http://example/3> <http://example/temporal> ,(spocq:make-date-time :year 2001 :month 01 :day 02
504
:second 03 :minute 04
506
(<http://example/4> <http://example/boolean> ,spocq.a:|false|)
507
(<http://example/4> <http://example/boolean> ,spocq.a:|true|)
508
(<_:node> |rdf|:|type| |owl|:|Class|))))
510
(write-rdf+xml *rdf+xml=test=field* *trace-output*)
512
(with-open-file (stream "/tmp/test.rdf" :direction :output :if-exists :supersede :if-does-not-exist :create)
513
(write-rdf+xml *rdf+xml=test=field* stream))