json-ld 0.0.8 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.markdown +36 -0
- data/{README → README.markdown} +1 -1
- data/VERSION +1 -1
- data/lib/json/ld.rb +92 -16
- data/lib/json/ld/api.rb +320 -0
- data/lib/json/ld/evaluation_context.rb +640 -0
- data/lib/json/ld/extensions.rb +6 -0
- data/lib/json/ld/reader.rb +120 -270
- data/lib/json/ld/writer.rb +210 -284
- metadata +53 -38
data/lib/json/ld/writer.rb
CHANGED
@@ -34,10 +34,8 @@ module JSON::LD
|
|
34
34
|
#
|
35
35
|
# The writer will add prefix definitions, and use them for creating @context definitions, and minting CURIEs
|
36
36
|
#
|
37
|
-
# @example Creating
|
37
|
+
# @example Creating @@context prefix definitions in output
|
38
38
|
# JSON::LD::Writer.buffer(
|
39
|
-
# :base_uri => "http://example.com/",
|
40
|
-
# :vocab => "http://example.net/"
|
41
39
|
# :prefixes => {
|
42
40
|
# nil => "http://example.com/ns#",
|
43
41
|
# :foaf => "http://xmlns.com/foaf/0.1/"}
|
@@ -47,7 +45,7 @@ module JSON::LD
|
|
47
45
|
# end
|
48
46
|
# end
|
49
47
|
#
|
50
|
-
# Select the :
|
48
|
+
# Select the :expand option to output JSON-LD in expanded form
|
51
49
|
#
|
52
50
|
# @see http://json-ld.org/spec/ED/20110507/
|
53
51
|
# @see http://json-ld.org/spec/ED/20110507/#the-normalization-algorithm
|
@@ -55,39 +53,18 @@ module JSON::LD
|
|
55
53
|
class Writer < RDF::Writer
|
56
54
|
format Format
|
57
55
|
|
58
|
-
# @attr [Graph] Graph of statements serialized
|
56
|
+
# @attr [RDF::Graph] Graph of statements serialized
|
59
57
|
attr :graph
|
60
|
-
|
61
|
-
attr
|
62
|
-
|
63
|
-
attr :vocab
|
64
|
-
|
65
|
-
# Type coersion to use for serialization. Defaults to DEFAULT_COERCION
|
66
|
-
#
|
67
|
-
# Maintained as a reverse mapping of `property` => `type`.
|
68
|
-
#
|
69
|
-
# @attr [Hash{String => String}]
|
70
|
-
attr :coerce, true
|
71
|
-
|
72
|
-
##
|
73
|
-
# Local implementation of ruby Hash class to allow for ordering in 1.8.x implementations.
|
74
|
-
#
|
75
|
-
# @return [Hash, InsertOrderPreservingHash]
|
76
|
-
def self.new_hash
|
77
|
-
if RUBY_VERSION < "1.9"
|
78
|
-
InsertOrderPreservingHash.new
|
79
|
-
else
|
80
|
-
Hash.new
|
81
|
-
end
|
82
|
-
end
|
83
|
-
def new_hash; self.class.new_hash; end
|
58
|
+
|
59
|
+
# @attr [EvaluationContext] context used to load and administer contexts
|
60
|
+
attr :context
|
84
61
|
|
85
62
|
##
|
86
63
|
# Return the pre-serialized Hash before turning into JSON
|
87
64
|
#
|
88
65
|
# @return [Hash]
|
89
66
|
def self.hash(*args, &block)
|
90
|
-
hash =
|
67
|
+
hash = Hash.new
|
91
68
|
self.new(hash, *args, &block)
|
92
69
|
hash
|
93
70
|
end
|
@@ -103,16 +80,24 @@ module JSON::LD
|
|
103
80
|
# the encoding to use on the output stream (Ruby 1.9+)
|
104
81
|
# @option options [Boolean] :canonicalize (false)
|
105
82
|
# whether to canonicalize literals when serializing
|
106
|
-
# @option options [Boolean] :normalize (false)
|
107
|
-
# Output document in [normalized form](http://json-ld.org/spec/latest/#normalization-1)
|
108
83
|
# @option options [Hash] :prefixes (Hash.new)
|
109
84
|
# the prefix mappings to use (not supported by all writers)
|
110
|
-
# @option options [#to_s] :base_uri (nil)
|
111
|
-
# Base IRI used for relativizing IRIs
|
112
|
-
# @option options [#to_s] :vocab (nil)
|
113
|
-
# Vocabulary prefix used for relativizing IRIs
|
114
85
|
# @option options [Boolean] :standard_prefixes (false)
|
115
86
|
# Add standard prefixes to @prefixes, if necessary.
|
87
|
+
# @option options [IO, Array, Hash, String, EvaluationContext] :context (Hash.new)
|
88
|
+
# context to use when serializing. Constructed context for native serialization.
|
89
|
+
# @option options [Boolean] :automatic (true)
|
90
|
+
# Automatically create context coercions and generate compacted form
|
91
|
+
# @option options [Boolean] :expand (false)
|
92
|
+
# Output document in [expanded form](http://json-ld.org/spec/latest/json-ld-api/#expansion)
|
93
|
+
# @option options [Boolean] :compact (false)
|
94
|
+
# Output document in [compacted form](http://json-ld.org/spec/latest/json-ld-api/#compaction).
|
95
|
+
# Requires a referenced evaluation context
|
96
|
+
# @option options [Boolean] :normalize (false)
|
97
|
+
# Output document in [normalized form](http://json-ld.org/spec/latest/json-ld-api/#normalization)
|
98
|
+
# @option options [IO, Array, Hash, String] :frame
|
99
|
+
# Output document in [framed form](http://json-ld.org/spec/latest/json-ld-api/#framing)
|
100
|
+
# using the referenced document as a frame.
|
116
101
|
# @yield [writer] `self`
|
117
102
|
# @yieldparam [RDF::Writer] writer
|
118
103
|
# @yieldreturn [void]
|
@@ -121,8 +106,8 @@ module JSON::LD
|
|
121
106
|
def initialize(output = $stdout, options = {}, &block)
|
122
107
|
super do
|
123
108
|
@graph = RDF::Graph.new
|
124
|
-
@
|
125
|
-
|
109
|
+
@options[:automatic] = true unless [:automatic, :expand, :compact, :frame, :normalize].any? {|k| options.has_key?(k)}
|
110
|
+
|
126
111
|
if block_given?
|
127
112
|
case block.arity
|
128
113
|
when 0 then instance_eval(&block)
|
@@ -138,7 +123,7 @@ module JSON::LD
|
|
138
123
|
# @param [Graph] graph
|
139
124
|
# @return [void]
|
140
125
|
def write_graph(graph)
|
141
|
-
|
126
|
+
debug {"Add graph #{graph.inspect}"}
|
142
127
|
@graph = graph
|
143
128
|
end
|
144
129
|
|
@@ -168,18 +153,32 @@ module JSON::LD
|
|
168
153
|
# @return [void]
|
169
154
|
# @see #write_triple
|
170
155
|
def write_epilogue
|
171
|
-
@base_uri = RDF::URI(@options[:base_uri]) if @options[:base_uri] && !@options[:normalize]
|
172
|
-
@vocab = @options[:vocab] unless @options[:normalize]
|
173
156
|
@debug = @options[:debug]
|
174
157
|
|
175
158
|
reset
|
159
|
+
|
160
|
+
raise RDF::WriterError, "Compaction requres a context" if @options[:compact] && !@options[:context]
|
176
161
|
|
177
|
-
|
162
|
+
@context = EvaluationContext.new(@options)
|
163
|
+
@context = @context.parse(@options[:context]) if @options[:context]
|
164
|
+
@context.language = @options[:language] if @options[:language]
|
165
|
+
@context.lists.each {|p| @list_range[p] = true}
|
166
|
+
|
167
|
+
debug {"\nserialize: graph: #{@graph.size}"}
|
168
|
+
debug {"=> options: #{@options.reject {|k,v| k == :debug}.inspect}"}
|
169
|
+
debug {"=> context: #{@context.inspect}"}
|
178
170
|
|
179
171
|
preprocess
|
180
|
-
|
181
|
-
#
|
182
|
-
|
172
|
+
|
173
|
+
# Update prefix mappings to those defined in context
|
174
|
+
@options[:prefixes] = {}
|
175
|
+
@context.iri_to_term.each_pair do |iri, term|
|
176
|
+
debug {"add prefix #{term.inspect} for #{iri}"}
|
177
|
+
prefix(term, iri) # Define for output
|
178
|
+
end
|
179
|
+
|
180
|
+
# Don't generate context for expanded or normalized output
|
181
|
+
json_hash = (@options[:expand] || @options[:normalize]) ? Hash.new : context.serialize(:depth => @depth)
|
183
182
|
|
184
183
|
elements = []
|
185
184
|
order_subjects.each do |subject|
|
@@ -190,16 +189,18 @@ module JSON::LD
|
|
190
189
|
|
191
190
|
return if elements.empty?
|
192
191
|
|
192
|
+
# If there are more than one top-level subjects, place in an array form
|
193
193
|
if elements.length == 1 && elements.first.is_a?(Hash)
|
194
194
|
json_hash.merge!(elements.first)
|
195
195
|
else
|
196
|
-
json_hash['@
|
196
|
+
json_hash['@id'] = elements
|
197
197
|
end
|
198
198
|
|
199
199
|
if @output.is_a?(Hash)
|
200
200
|
@output.merge!(json_hash)
|
201
201
|
else
|
202
202
|
json_state = if @options[:normalize]
|
203
|
+
# Normalization uses a compressed form
|
203
204
|
JSON::State.new(
|
204
205
|
:indent => "",
|
205
206
|
:space => "",
|
@@ -223,35 +224,24 @@ module JSON::LD
|
|
223
224
|
##
|
224
225
|
# Returns the representation of a IRI reference.
|
225
226
|
#
|
226
|
-
# Spec confusion: should a subject
|
227
|
+
# Spec confusion: should a subject IRI be normalized?
|
227
228
|
#
|
228
229
|
# @param [RDF::URI] value
|
229
230
|
# @param [Hash{Symbol => Object}] options
|
230
231
|
# @option options [:subject, :predicate, :object] position
|
231
232
|
# Useful when determining how to serialize.
|
232
233
|
# @option options [RDF::URI] property
|
233
|
-
# Property for object reference, which can be used to return
|
234
|
-
# bare strings, rather than {"iri":}
|
234
|
+
# Property for object reference, which can be used to return bare strings
|
235
235
|
# @return [Object]
|
236
|
-
def
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
when :predicate
|
243
|
-
# attempt vocab replacement
|
244
|
-
short = '@type' if value == RDF.type
|
245
|
-
short ||= value.to_s.sub(@vocab.to_s, "")
|
246
|
-
short == value.to_s ? (get_curie(value) || value.to_s) : short
|
247
|
-
else
|
248
|
-
# Encode like a subject
|
249
|
-
iri_range?(options[:property]) ?
|
250
|
-
format_uri(value, :position => :subject) :
|
251
|
-
{'@iri' => format_uri(value, :position => :subject)}
|
236
|
+
def format_iri(value, options = {})
|
237
|
+
debug {"format_iri(#{options.inspect}, #{value.inspect})"}
|
238
|
+
|
239
|
+
result = context.compact_iri(value, {:depth => @depth}.merge(options))
|
240
|
+
unless options[:position] != :object || iri_range?(options[:property])
|
241
|
+
result = {"@id" => result}
|
252
242
|
end
|
253
243
|
|
254
|
-
|
244
|
+
debug {"=> #{result.inspect}"}
|
255
245
|
result
|
256
246
|
end
|
257
247
|
|
@@ -260,9 +250,9 @@ module JSON::LD
|
|
260
250
|
# @param [Hash{Symbol => Object}] options
|
261
251
|
# @return [String]
|
262
252
|
# @raise [NotImplementedError] unless implemented in subclass
|
263
|
-
# @
|
253
|
+
# @see {#format\_iri}
|
264
254
|
def format_node(value, options = {})
|
265
|
-
|
255
|
+
format_iri(value, options)
|
266
256
|
end
|
267
257
|
|
268
258
|
##
|
@@ -274,137 +264,88 @@ module JSON::LD
|
|
274
264
|
# Property referencing literal for type coercion
|
275
265
|
# @return [Object]
|
276
266
|
def format_literal(literal, options = {})
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
end
|
267
|
+
debug {"format_literal(#{options.inspect}, #{literal.inspect})"}
|
268
|
+
|
269
|
+
value = Hash.new
|
270
|
+
value['@literal'] = literal.value
|
271
|
+
value['@type'] = literal.datatype.to_s if literal.has_datatype?
|
272
|
+
value['@language'] = literal.language.to_s if literal.has_language?
|
284
273
|
|
285
|
-
case literal
|
286
|
-
when RDF::Literal::Integer, RDF::Literal::
|
274
|
+
result = case literal
|
275
|
+
when RDF::Literal::Boolean, RDF::Literal::Integer, RDF::Literal::Double
|
287
276
|
literal.object
|
288
|
-
|
289
|
-
|
290
|
-
# Datatype coercion where literal has the same datatype
|
291
|
-
literal.value
|
292
|
-
else
|
293
|
-
format_literal(literal, :normal => true)
|
294
|
-
end
|
277
|
+
else
|
278
|
+
context.compact_value(options[:property], value, {:depth => @depth}.merge(options))
|
295
279
|
end
|
280
|
+
|
281
|
+
debug {"=> #{result.inspect}"}
|
282
|
+
result
|
296
283
|
end
|
297
284
|
|
298
285
|
##
|
299
286
|
# Serialize an RDF list
|
287
|
+
#
|
300
288
|
# @param [RDF::URI] object
|
301
289
|
# @param [Hash{Symbol => Object}] options
|
302
290
|
# @option options [RDF::URI] property
|
303
|
-
# Property referencing literal for type coercion
|
291
|
+
# Property referencing literal for type and list coercion
|
304
292
|
# @return [Hash{"@list" => Array<Object>}]
|
305
293
|
def format_list(object, options = {})
|
306
294
|
predicate = options[:property]
|
307
|
-
list =
|
295
|
+
list = RDF::List.new(object, @graph)
|
296
|
+
ary = []
|
308
297
|
|
309
|
-
|
298
|
+
debug {"format_list(#{list.inspect}, #{predicate})"}
|
310
299
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
add_debug {"format_list serialize #{item.inspect}"}
|
318
|
-
list << if predicate || item.literal?
|
319
|
-
property(predicate, item)
|
300
|
+
depth do
|
301
|
+
list.each_statement do |st|
|
302
|
+
next unless st.predicate == RDF.first
|
303
|
+
debug {" format_list this: #{st.subject} first: #{st.object}"}
|
304
|
+
ary << if predicate || st.object.literal?
|
305
|
+
property(predicate, st.object)
|
320
306
|
else
|
321
|
-
subject(
|
307
|
+
subject(st.object)
|
322
308
|
end
|
309
|
+
subject_done(st.subject)
|
323
310
|
end
|
324
|
-
object = p.fetch(RDF.rest.to_s, []).first
|
325
311
|
end
|
326
|
-
@depth -= 1
|
327
312
|
|
328
|
-
# Returns
|
329
|
-
|
330
|
-
{
|
313
|
+
# Returns
|
314
|
+
ary = {'@list' => ary} unless predicate && list_range?(predicate)
|
315
|
+
debug {"format_list => #{ary.inspect}"}
|
316
|
+
ary
|
331
317
|
end
|
332
318
|
|
333
319
|
private
|
334
|
-
##
|
335
|
-
# Generate @context
|
336
|
-
# @return [Hash]
|
337
|
-
def start_document
|
338
|
-
ctx = new_hash
|
339
|
-
ctx['@base'] = base_uri.to_s if base_uri
|
340
|
-
ctx['@vocab'] = vocab.to_s if vocab
|
341
|
-
|
342
|
-
# Prefixes
|
343
|
-
prefixes.keys.sort {|a,b| a.to_s <=> b.to_s}.each do |k|
|
344
|
-
next if DEFAULT_CONTEXT.has_key?(k.to_s)
|
345
|
-
add_debug {"prefix[#{k}] => #{prefixes[k]}"}
|
346
|
-
ctx[k.to_s] = prefixes[k].to_s
|
347
|
-
end
|
348
|
-
|
349
|
-
# Coerce
|
350
|
-
add_debug {"start_doc: coerce= #{coerce.inspect}"}
|
351
|
-
unless coerce == DEFAULT_COERCE
|
352
|
-
c_h = new_hash
|
353
|
-
coerce.keys.sort.each do |k|
|
354
|
-
next if ['@type', RDF.type.to_s].include?(k.to_s)
|
355
|
-
next if [DEFAULT_COERCE[k], false, RDF::XSD.integer.to_s, RDF::XSD.boolean.to_s].include?(coerce[k])
|
356
|
-
k_iri = k == '@iri' ? '@iri' : format_uri(k, :position => :predicate)
|
357
|
-
d_iri = format_uri(coerce[k], :position => :subject)
|
358
|
-
add_debug {"coerce[#{k_iri}] => #{d_iri}, k=#{k.inspect}"}
|
359
|
-
case c_h[d_iri]
|
360
|
-
when nil
|
361
|
-
c_h[d_iri] = k_iri
|
362
|
-
when Array
|
363
|
-
c_h[d_iri] << k_iri
|
364
|
-
else
|
365
|
-
c_h[d_iri] = [c_h[d_iri], k_iri]
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
ctx['@coerce'] = c_h unless c_h.empty?
|
370
|
-
end
|
371
|
-
|
372
|
-
add_debug {"start_doc: context=#{ctx.inspect}"}
|
373
|
-
|
374
|
-
# Return hash with @context, or empty
|
375
|
-
r = new_hash
|
376
|
-
r['@context'] = ctx unless ctx.empty?
|
377
|
-
r
|
378
|
-
end
|
379
|
-
|
380
320
|
# Perform any preprocessing of statements required
|
381
321
|
def preprocess
|
382
|
-
# Load defined prefixes
|
383
|
-
(@options[:prefixes] || {}).each_pair do |k, v|
|
384
|
-
@iri_to_prefix[v.to_s] = k
|
385
|
-
end
|
386
|
-
@options[:prefixes] = new_hash # Will define actual used when matched
|
387
|
-
|
388
322
|
@graph.each {|statement| preprocess_statement(statement)}
|
389
323
|
end
|
390
324
|
|
391
325
|
# Perform any statement preprocessing required. This is used to perform reference counts and determine required
|
392
326
|
# prefixes.
|
327
|
+
#
|
393
328
|
# @param [Statement] statement
|
394
329
|
def preprocess_statement(statement)
|
395
|
-
|
330
|
+
debug {"preprocess: #{statement.inspect}"}
|
396
331
|
references = ref_count(statement.object) + 1
|
397
332
|
@references[statement.object] = references
|
398
333
|
@subjects[statement.subject] = true
|
399
334
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
335
|
+
depth do
|
336
|
+
# Pre-fetch qnames, to fill prefixes
|
337
|
+
format_iri(statement.subject, :position => :subject)
|
338
|
+
format_iri(statement.predicate, :position => :predicate)
|
339
|
+
|
340
|
+
# To figure out coercion requirements
|
341
|
+
if statement.object.literal?
|
342
|
+
format_literal(statement.object, :property => statement.predicate)
|
343
|
+
datatype_range?(statement.predicate)
|
344
|
+
else
|
345
|
+
format_iri(statement.object, :position => :object)
|
346
|
+
iri_range?(statement.predicate)
|
347
|
+
end
|
348
|
+
list_range?(statement.predicate)
|
408
349
|
end
|
409
350
|
|
410
351
|
@references[statement.predicate] = ref_count(statement.predicate) + 1
|
@@ -414,53 +355,53 @@ module JSON::LD
|
|
414
355
|
# Option contains referencing property, if this is recursive
|
415
356
|
# @return [Hash]
|
416
357
|
def subject(subject, options = {})
|
417
|
-
defn =
|
358
|
+
defn = Hash.new
|
418
359
|
|
419
360
|
raise RDF::WriterError, "Illegal use of subject #{subject.inspect}, not supported" unless subject.resource?
|
420
361
|
|
421
362
|
subject_done(subject)
|
422
363
|
properties = @graph.properties(subject)
|
423
|
-
|
364
|
+
debug {"subject: #{subject.inspect}, props: #{properties.inspect}"}
|
424
365
|
|
425
366
|
@graph.query(:subject => subject).each do |st|
|
426
367
|
raise RDF::WriterError, "Illegal use of predicate #{st.predicate.inspect}, not supported in RDF/XML" unless st.predicate.uri?
|
427
368
|
end
|
428
369
|
|
429
|
-
if subject.node? && ref_count(subject) > (options[:property] ? 1 : 0) && options[:
|
370
|
+
if subject.node? && ref_count(subject) > (options[:property] ? 1 : 0) && options[:expand]
|
430
371
|
raise RDF::WriterError, "Can't serialize named node when normalizing"
|
431
372
|
end
|
432
373
|
|
433
374
|
# Subject may be a list
|
434
375
|
if is_valid_list?(subject)
|
435
|
-
|
436
|
-
defn['@
|
376
|
+
debug "subject is a list"
|
377
|
+
defn['@id'] = format_list(subject)
|
437
378
|
properties.delete(RDF.first.to_s)
|
438
379
|
properties.delete(RDF.rest.to_s)
|
439
380
|
|
440
381
|
# Special case, if there are no properties, then we can just serialize the list itself
|
441
382
|
return defn if properties.empty?
|
442
383
|
elsif subject.uri? || ref_count(subject) > 1
|
443
|
-
|
384
|
+
debug "subject is an iri or it's a node referenced multiple times"
|
444
385
|
# Don't need to set subject if it's a Node without references
|
445
|
-
defn['@
|
386
|
+
defn['@id'] = format_iri(subject, :position => :subject)
|
446
387
|
else
|
447
|
-
|
388
|
+
debug "subject is an unreferenced BNode"
|
448
389
|
end
|
449
390
|
|
450
391
|
prop_list = order_properties(properties)
|
451
|
-
|
392
|
+
debug {"=> property order: #{prop_list.inspect}"}
|
452
393
|
|
453
394
|
prop_list.each do |prop|
|
454
395
|
predicate = RDF::URI.intern(prop)
|
455
396
|
|
456
|
-
p_iri =
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
397
|
+
p_iri = format_iri(predicate, :position => :predicate)
|
398
|
+
depth do
|
399
|
+
defn[p_iri] = property(predicate, properties[prop])
|
400
|
+
debug {"prop(#{p_iri}) => #{properties[prop]} => #{defn[p_iri].inspect}"}
|
401
|
+
end
|
461
402
|
end
|
462
403
|
|
463
|
-
|
404
|
+
debug {"subject: #{subject} has defn: #{defn.inspect}"}
|
464
405
|
defn
|
465
406
|
end
|
466
407
|
|
@@ -484,55 +425,13 @@ module JSON::LD
|
|
484
425
|
if is_valid_list?(objects)
|
485
426
|
format_list(objects, :property => predicate)
|
486
427
|
elsif is_done?(objects) || !@subjects.include?(objects)
|
487
|
-
|
428
|
+
format_iri(objects, :position => :object, :property => predicate)
|
488
429
|
else
|
489
430
|
subject(objects, :property => predicate)
|
490
431
|
end
|
491
432
|
end
|
492
433
|
end
|
493
434
|
|
494
|
-
##
|
495
|
-
# Return a CURIE for the IRI, or nil. Adds namespace of CURIE to defined prefixes
|
496
|
-
# @param [RDF::Resource] resource
|
497
|
-
# @return [String, nil] value to use to identify IRI
|
498
|
-
def get_curie(resource)
|
499
|
-
add_debug {"get_curie(#{resource.inspect})"}
|
500
|
-
case resource
|
501
|
-
when RDF::Node
|
502
|
-
return resource.to_s
|
503
|
-
when String
|
504
|
-
iri = resource
|
505
|
-
resource = RDF::URI(resource)
|
506
|
-
return nil unless resource.absolute?
|
507
|
-
when RDF::URI
|
508
|
-
iri = resource.to_s
|
509
|
-
return iri if options[:normalize]
|
510
|
-
else
|
511
|
-
return nil
|
512
|
-
end
|
513
|
-
|
514
|
-
curie = case
|
515
|
-
when @iri_to_curie.has_key?(iri)
|
516
|
-
return @iri_to_curie[iri]
|
517
|
-
when u = @iri_to_prefix.keys.detect {|u| iri.index(u.to_s) == 0}
|
518
|
-
# Use a defined prefix
|
519
|
-
prefix = @iri_to_prefix[u]
|
520
|
-
prefix(prefix, u) # Define for output
|
521
|
-
iri.sub(u.to_s, "#{prefix}:")
|
522
|
-
when @options[:standard_prefixes] && vocab = RDF::Vocabulary.detect {|v| iri.index(v.to_uri.to_s) == 0}
|
523
|
-
prefix = vocab.__name__.to_s.split('::').last.downcase
|
524
|
-
@iri_to_prefix[vocab.to_uri.to_s] = prefix
|
525
|
-
prefix(prefix, vocab.to_uri) # Define for output
|
526
|
-
iri.sub(vocab.to_uri.to_s, "#{prefix}:")
|
527
|
-
else
|
528
|
-
nil
|
529
|
-
end
|
530
|
-
|
531
|
-
@iri_to_curie[iri] = curie
|
532
|
-
rescue Addressable::URI::InvalidURIError => e
|
533
|
-
raise RDF::WriterError, "Invalid IRI #{resource.inspect}: #{e.message}"
|
534
|
-
end
|
535
|
-
|
536
435
|
##
|
537
436
|
# Take a hash from predicate IRIs to lists of values.
|
538
437
|
# Sort the lists of values. Return a sorted list of properties.
|
@@ -542,8 +441,8 @@ module JSON::LD
|
|
542
441
|
# Make sorted list of properties
|
543
442
|
prop_list = []
|
544
443
|
|
545
|
-
properties.keys.sort do |a,b|
|
546
|
-
|
444
|
+
properties.keys.sort do |a, b|
|
445
|
+
format_iri(a, :position => :predicate) <=> format_iri(b, :position => :predicate)
|
547
446
|
end.each do |prop|
|
548
447
|
prop_list << prop.to_s
|
549
448
|
end
|
@@ -553,7 +452,6 @@ module JSON::LD
|
|
553
452
|
|
554
453
|
# Order subjects for output. Override this to output subjects in another order.
|
555
454
|
#
|
556
|
-
# Uses #base_uri.
|
557
455
|
# @return [Array<Resource>] Ordered list of subjects
|
558
456
|
def order_subjects
|
559
457
|
seen = {}
|
@@ -561,14 +459,8 @@ module JSON::LD
|
|
561
459
|
|
562
460
|
return @subjects.keys.sort do |a,b|
|
563
461
|
format_iri(a, :position => :subject) <=> format_iri(b, :position => :subject)
|
564
|
-
end
|
462
|
+
end unless @options[:automatic]
|
565
463
|
|
566
|
-
# Start with base_uri
|
567
|
-
if base_uri && @subjects.keys.include?(base_uri)
|
568
|
-
subjects << base_uri
|
569
|
-
seen[base_uri] = true
|
570
|
-
end
|
571
|
-
|
572
464
|
# Sort subjects by resources over bnodes, ref_counts and the subject URI itself
|
573
465
|
recursable = @subjects.keys.
|
574
466
|
select {|s| !seen.include?(s)}.
|
@@ -589,16 +481,30 @@ module JSON::LD
|
|
589
481
|
# @param [RDF::URI] predicate
|
590
482
|
# @return [Boolean]
|
591
483
|
def iri_range?(predicate)
|
592
|
-
return false if predicate.nil? || @options[:
|
484
|
+
return false if predicate.nil? || [RDF.first, RDF.rest].include?(predicate) || @options[:expand]
|
485
|
+
return true if predicate == RDF.type
|
593
486
|
|
594
|
-
unless coerce
|
595
|
-
|
596
|
-
|
597
|
-
|
487
|
+
unless context.coerce(predicate)
|
488
|
+
not_iri = !@options[:automatic]
|
489
|
+
#debug {" (automatic) = #{(!not_iri).inspect}"}
|
490
|
+
|
491
|
+
# Any literal object makes it not so
|
492
|
+
not_iri ||= @graph.query(:predicate => predicate).to_a.any? do |st|
|
493
|
+
l = RDF::List.new(st.object, @graph)
|
494
|
+
#debug {" o.literal? #{st.object.literal?.inspect}"}
|
495
|
+
#debug {" l.valid? #{l.valid?.inspect}"}
|
496
|
+
#debug {" l.any.valid? #{l.to_a.any?(&:literal?).inspect}"}
|
497
|
+
st.object.literal? || (l.valid? && l.to_a.any?(&:literal?))
|
498
|
+
end
|
499
|
+
#debug {" (literal) = #{(!not_iri).inspect}"}
|
500
|
+
|
501
|
+
# FIXME: detect when values are all represented through chaining
|
502
|
+
|
503
|
+
context.coerce(predicate, not_iri ? false : '@id')
|
598
504
|
end
|
599
505
|
|
600
|
-
|
601
|
-
coerce
|
506
|
+
debug {"iri_range(#{predicate}) = #{context.coerce(predicate).inspect}"}
|
507
|
+
context.coerce(predicate) == '@id'
|
602
508
|
end
|
603
509
|
|
604
510
|
##
|
@@ -606,23 +512,56 @@ module JSON::LD
|
|
606
512
|
# @param [RDF::URI] predicate
|
607
513
|
# @return [Boolean]
|
608
514
|
def datatype_range?(predicate)
|
609
|
-
unless coerce
|
515
|
+
unless context.coerce(predicate)
|
610
516
|
# objects of all statements with the predicate must be literal
|
611
517
|
# and have the same non-nil datatype
|
612
518
|
dt = nil
|
613
|
-
@
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
519
|
+
if @options[:automatic]
|
520
|
+
@graph.query(:predicate => predicate) do |st|
|
521
|
+
debug {"datatype_range? literal? #{st.object.literal?.inspect} dt? #{(st.object.literal? && st.object.has_datatype?).inspect}"}
|
522
|
+
if st.object.literal? && st.object.has_datatype?
|
523
|
+
dt = st.object.datatype.to_s if dt.nil?
|
524
|
+
debug {"=> dt: #{st.object.datatype}"}
|
525
|
+
dt = false unless dt == st.object.datatype.to_s
|
526
|
+
else
|
527
|
+
dt = false
|
528
|
+
end
|
529
|
+
end
|
530
|
+
# Cause necessary prefixes to be output
|
531
|
+
format_iri(dt, :position => :datatype) if dt && !NATIVE_DATATYPES.include?(dt.to_s)
|
532
|
+
debug {"range(#{predicate}) = #{dt.inspect}"}
|
533
|
+
else
|
534
|
+
dt = false
|
535
|
+
end
|
536
|
+
context.coerce(predicate, dt)
|
537
|
+
end
|
538
|
+
|
539
|
+
context.coerce(predicate)
|
540
|
+
end
|
541
|
+
|
542
|
+
##
|
543
|
+
# Is every use of the predicate an RDF Collection?
|
544
|
+
#
|
545
|
+
# @param [RDF::URI] predicate
|
546
|
+
# @return [Boolean]
|
547
|
+
def list_range?(predicate)
|
548
|
+
return false if [RDF.first, RDF.rest].include?(predicate)
|
549
|
+
|
550
|
+
unless @list_range.include?(predicate.to_s)
|
551
|
+
# objects of all statements with the predicate must be a list
|
552
|
+
@list_range[predicate.to_s] = if @options[:automatic]
|
553
|
+
@graph.query(:predicate => predicate).to_a.all? do |st|
|
554
|
+
is_valid_list?(st.object)
|
619
555
|
end
|
556
|
+
else
|
557
|
+
false
|
620
558
|
end
|
621
|
-
|
622
|
-
|
559
|
+
context.list(predicate, true) if @list_range[predicate.to_s]
|
560
|
+
|
561
|
+
debug {"list(#{predicate}) = #{@list_range[predicate.to_s].inspect}"}
|
623
562
|
end
|
624
563
|
|
625
|
-
|
564
|
+
@list_range[predicate.to_s]
|
626
565
|
end
|
627
566
|
|
628
567
|
# Reset internal helper instance variables
|
@@ -631,46 +570,13 @@ module JSON::LD
|
|
631
570
|
@references = {}
|
632
571
|
@serialized = {}
|
633
572
|
@subjects = {}
|
634
|
-
@
|
573
|
+
@list_range = {}
|
635
574
|
end
|
636
575
|
|
637
|
-
# Add debug event to debug array, if specified
|
638
|
-
#
|
639
|
-
# @param [String] message
|
640
|
-
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
641
|
-
def add_debug(message = "")
|
642
|
-
return unless ::JSON::LD.debug? || @options[:debug]
|
643
|
-
message = message + yield if block_given?
|
644
|
-
msg = "#{" " * @depth * 2}#{message}"
|
645
|
-
STDERR.puts msg if ::JSON::LD::debug?
|
646
|
-
@debug << msg if @debug.is_a?(Array)
|
647
|
-
end
|
648
|
-
|
649
576
|
# Checks if l is a valid RDF list, i.e. no nodes have other properties.
|
650
577
|
def is_valid_list?(l)
|
651
|
-
|
652
|
-
|
653
|
-
add_debug {"is_valid_list: false, #{l.inspect}: #{props.inspect}"}
|
654
|
-
return false
|
655
|
-
end
|
656
|
-
|
657
|
-
while l && l != RDF.nil do
|
658
|
-
#add_debug {"is_valid_list(length): #{props.length}"}
|
659
|
-
return false unless props.has_key?(RDF.first.to_s) && props.has_key?(RDF.rest.to_s)
|
660
|
-
n = props[RDF.rest.to_s]
|
661
|
-
unless n.is_a?(Array) && n.length == 1
|
662
|
-
add_debug {"is_valid_list: false, #{n.inspect}"}
|
663
|
-
return false
|
664
|
-
end
|
665
|
-
l = n.first
|
666
|
-
unless l.node? || l == RDF.nil
|
667
|
-
add_debug {"is_valid_list: false, #{l.inspect}"}
|
668
|
-
return false
|
669
|
-
end
|
670
|
-
props = @graph.properties(l)
|
671
|
-
end
|
672
|
-
add_debug {"is_valid_list: valid"}
|
673
|
-
true
|
578
|
+
#debug {"is_valid_list: #{l.inspect}"}
|
579
|
+
return RDF::List.new(l, @graph).valid?
|
674
580
|
end
|
675
581
|
|
676
582
|
def is_done?(subject)
|
@@ -681,6 +587,26 @@ module JSON::LD
|
|
681
587
|
def subject_done(subject)
|
682
588
|
@serialized[subject] = true
|
683
589
|
end
|
590
|
+
|
591
|
+
# Add debug event to debug array, if specified
|
592
|
+
#
|
593
|
+
# @param [String] message
|
594
|
+
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
595
|
+
def debug(*args)
|
596
|
+
return unless ::JSON::LD.debug? || @options[:debug]
|
597
|
+
message = " " * @depth * 2 + (args.empty? ? "" : args.join(": "))
|
598
|
+
message += yield if block_given?
|
599
|
+
puts message if JSON::LD::debug?
|
600
|
+
@options[:debug] << message if @options[:debug].is_a?(Array)
|
601
|
+
end
|
602
|
+
|
603
|
+
# Increase depth around a method invocation
|
604
|
+
def depth
|
605
|
+
@depth += 1
|
606
|
+
ret = yield
|
607
|
+
@depth -= 1
|
608
|
+
ret
|
609
|
+
end
|
684
610
|
end
|
685
611
|
end
|
686
612
|
|