guile-jsonld/jsonld/context-processing.scm

context-processing.scm

1
;;;; Copyright (C) 2019, 2020 Julien Lepiller <julien@lepiller.eu>
2
;;;; 
3
;;;; This library is free software; you can redistribute it and/or
4
;;;; modify it under the terms of the GNU Lesser General Public
5
;;;; License as published by the Free Software Foundation; either
6
;;;; version 3 of the License, or (at your option) any later version.
7
;;;; 
8
;;;; This library is distributed in the hope that it will be useful,
9
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
10
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
;;;; Lesser General Public License for more details.
12
;;;; 
13
;;;; You should have received a copy of the GNU Lesser General Public
14
;;;; License along with this library; if not, write to the Free Software
15
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
;;;; 
17
18
(define-module (jsonld context-processing)
19
  #:use-module (iri iri)
20
  #:use-module (jsonld context)
21
  #:use-module (jsonld create-term-definition)
22
  #:use-module (jsonld download)
23
  #:use-module (jsonld iri-expansion)
24
  #:use-module (jsonld json)
25
  #:use-module (jsonld options)
26
  #:use-module (json)
27
  #:use-module (web uri)
28
  #:use-module (rnrs bytevectors)
29
  #:use-module (ice-9 match)
30
  #:use-module (srfi srfi-9)
31
  #:export (context-processing))
32
33
(define* (context-processing
34
           active-context local-context base-url
35
           #:key (remote-contexts '()) (override-protected? #f) (propagate? #t)
36
           (validate-scoped-context? #t)
37
           (options (new-jsonld-options)))
38
  "Process a context.  This is an implementation of the context processing
39
algorithm defined in the JsonLD API specification.
40
41
See @url{https://www.w3.org/TR/2014/REC-json-ld-api-20140116}."
42
  ;; 1
43
  (define result active-context)
44
  ;; 2
45
  (when (json-has-key? local-context "@propagate")
46
    (set! propagate? (assoc-ref local-context "@propagate")))
47
  ;; 3
48
  (unless (or propagate? (not-null-or-false (active-context-previous active-context)))
49
    (set! result (update-active-context result #:previous active-context)))
50
  ;; 4
51
  (if (json-array? local-context)
52
      (set! local-context (array->list local-context))
53
      (set! local-context (list local-context)))
54
  ;; 5
55
  (for-each
56
    (lambda (context)
57
      (match context
58
        ;; 5.1
59
        ((? json-null? null)
60
         (unless (or override-protected?
61
                     (null? (filter
62
                              term-definition-protected?
63
                              (map cdr (active-context-definitions result)))))
64
           (throw 'invalid-context-nullification))
65
         (let ((original-base (active-context-original-base active-context)))
66
           (if propagate?
67
             (set! result (new-active-context #:previous result
68
                                              #:base original-base
69
                                              #:original-base original-base))
70
             (set! result (new-active-context #:base original-base
71
                                              #:original-base original-base)))))
72
        ;; 5.2
73
        ((? string? context)
74
         ;; 5.2.1
75
         (set! context (resolve-iri base-url context))
76
         (unless (absolute-iri? context)
77
           (throw 'loading-document-failed context))
78
         ;; 5.2.2
79
         ;; A processor-defined limit, here we choose 10
80
         (when (> (length remote-contexts) 10)
81
           (throw 'context-overflow-error))
82
         (set! remote-contexts (cons context remote-contexts))
83
         ;; 5.2.3
84
         (when validate-scoped-context?
85
           ;; 5.2.4
86
           (let ((context-document #f))
87
             (catch #t
88
               (lambda ()
89
                 (set! context-document
90
                   ((jsonld-options-document-loader options)
91
                      context
92
                      #:profile "http://www.w3.org/ns/json-ld#context"
93
                      #:request-profile "http://www.w3.org/ns/json-ld#context"))
94
                 (set! context (json-document-document context-document)))
95
               (lambda (key . value)
96
                 (apply throw 'loading-remote-context-failed key value)))
97
             ;; 5.2.5
98
             (if (json-has-key? context "@context")
99
               (set! context (assoc-ref context "@context"))
100
               (throw 'invalid-remote-context))
101
             ;; 5.2.6
102
             (set! result (context-processing
103
                            result context
104
                            (json-document-document-url context-document)
105
                            #:remote-contexts remote-contexts
106
                            #:validate-scoped-context? validate-scoped-context?
107
                            #:options options)))))
108
        ;; 5.4: if it's a Json object (also see below, if it's not, for 5.3)
109
        ((? json-object? context)
110
         ;; 5.5: If it has an @version key...
111
         (when (json-has-key? context "@version")
112
           ;; 5.5.1
113
           (unless (equal? (assoc-ref context "@version") 1.1)
114
             (throw 'invalid-@version-value (assoc-ref context "@version")))
115
           ;; 5.5.2
116
           (when (processing-mode-1.0? (jsonld-options-processing-mode options))
117
             (throw 'processing-mode-conflict)))
118
119
         ;; 5.6: If it has an @import key...
120
         (when (json-has-key? context "@import")
121
           ;; 5.6.1
122
           (when (processing-mode-1.0? (jsonld-options-processing-mode options))
123
             (throw 'invalid-context-entry))
124
           ;; 5.6.2
125
           (unless (string? (assoc-ref context "@import"))
126
             (throw 'invalid-@import-value))
127
           ;; 5.6.3
128
           (let ((import (resolve-iri
129
                           base-url
130
                           (assoc-ref context "@import"))))
131
             ;; 5.6.4
132
             (catch #t
133
               (lambda ()
134
                 (set! import ((jsonld-options-document-loader options)
135
                                 import
136
                                 #:profile "http://www.w3.org/ns/json-ld#context"
137
                                 #:request-profile "http://www.w3.org/ns/json-ld#context"))
138
                 (set! import (json-document-document import)))
139
               (lambda (key . value)
140
                 (apply throw 'loading-remote-context-failed key value)))
141
             ;; 5.6.6
142
             (unless (and (json-has-key? import "@context")
143
                          (json-object? (assoc-ref import "@context")))
144
               (throw 'invalid-remote-context import))
145
             (let ((import-context (assoc-ref import "@context")))
146
               ;; 5.6.7
147
               (when (json-has-key? import-context "@import")
148
                 (throw 'invalid-context-entry))
149
               ;; 5.6.8
150
               (set! context (merge-json context import-context)))))
151
152
         ;; 5.7: If it has an @base key...
153
         (when (and (json-has-key? context "@base") (null? remote-contexts))
154
           ;; 5.7.1
155
           (let ((value (assoc-ref context "@base")))
156
             (cond
157
               ;; 5.7.2
158
               ((json-null? value)
159
                (set! result (update-active-context result #:base json-null)))
160
               ;; 5.7.3
161
               ((absolute-iri? value)
162
                (set! result (update-active-context result #:base value)))
163
               (else
164
                 (let ((iri (resolve-iri (active-context-base result) value)))
165
                   (if (and (absolute-iri? iri)
166
                            (not-null-or-false (active-context-base result)))
167
                     ;; 5.7.4
168
                     (set! result (update-active-context result #:base iri))
169
                     ;; 5.7.5
170
                     (throw 'invalid-base-iri iri (active-context-base result))))))))
171
172
         ;; 5.8: If it has an @vocab key...
173
         (when (json-has-key? context "@vocab")
174
           ;; 5.8.1
175
           (let ((value (assoc-ref context "@vocab")))
176
             (if (json-null? value)
177
               ;; 5.8.2
178
               (set! result (update-active-context result #:vocab #f))
179
               ;; 5.8.3
180
               (let ((value
181
                       (assoc-ref
182
                         (iri-expansion result value
183
                                        #:vocab? #t
184
                                        #:document-relative? #t)
185
                         "iri")))
186
                 (if (or (absolute-iri? value) (blank-node? value))
187
                   (set! result (update-active-context result #:vocab value))
188
                   (throw 'invalid-vocab-mapping value))))))
189
190
         ;; 5.9: If it has an @language key...
191
         (when (json-has-key? context "@language")
192
           ;; 5.9.1
193
           (let ((value (assoc-ref context "@language")))
194
             (if (json-null? value)
195
               ;; 5.9.2
196
               (set! result (update-active-context result #:language #f))
197
               ;; 5.9.3
198
               (if (string? value)
199
                 (set! result (update-active-context result #:language value))
200
                 (throw 'invalid-default-language)))))
201
202
         ;; 5.10: If it has an @direction key...
203
         (when (json-has-key? context "@direction")
204
           ;; 5.10.1
205
           (when (processing-mode-1.0? (jsonld-options-processing-mode options))
206
             (throw 'invalid-context-entry))
207
           (let ((value (assoc-ref context "@direction")))
208
             (if (json-null? value)
209
               ;; 5.10.2
210
               (set! result (update-active-context result #:direction #f))
211
               ;; 5.10.3
212
               (if (member value '("ltr" "rtl"))
213
                 (set! result (update-active-context result #:direction value))
214
                 (throw 'invalid-base-direction value)))))
215
216
         ;; 5.11: If it has an @propagate key...
217
         (when (json-has-key? context "@propagate")
218
           ;; 5.11.1
219
           (when (processing-mode-1.0? (jsonld-options-processing-mode options))
220
             (throw 'invalid-context-entry))
221
           ;; 5.11.2
222
           (unless (member (assoc-ref context "@propagate") '(#t #f))
223
             (throw 'invalid-@propagate-value)))
224
225
         ;; and now loop over key-value pair that are not @base, @vocab nor @language
226
         ;; 5.12
227
         (let ((defined '()))
228
           ;; 5.13
229
           (for-each-pair
230
             (lambda (key value)
231
               (unless (member key '("@base" "@direction" "@import" "@language"
232
                                     "@propagate" "@protected" "@version"
233
                                     "@vocab"))
234
                 (let* ((term-res (create-term-definition
235
                                    result context key defined
236
                                    #:base-url base-url
237
                                    #:protected? (assoc-ref context "@protected")
238
                                    #:override-protected? override-protected?
239
                                    #:remote-contexts remote-contexts
240
                                    #:validate-scoped-context? validate-scoped-context?
241
                                    #:options options)))
242
                   (set! defined (assoc-ref term-res "defined"))
243
                   (set! result (assoc-ref term-res "active-context")))))
244
             context)))
245
        ;; 5.3: if it's not a Json object
246
        (_ (throw 'invalid-local-context context))))
247
    local-context)
248
  result)
249