rdf-n3 3.0.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +198 -76
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/lib/rdf/n3/algebra/builtin.rb +79 -0
- data/lib/rdf/n3/algebra/formula.rb +446 -0
- data/lib/rdf/n3/algebra/list/append.rb +42 -0
- data/lib/rdf/n3/algebra/list/first.rb +24 -0
- data/lib/rdf/n3/algebra/list/in.rb +48 -0
- data/lib/rdf/n3/algebra/list/iterate.rb +96 -0
- data/lib/rdf/n3/algebra/list/last.rb +24 -0
- data/lib/rdf/n3/algebra/list/length.rb +24 -0
- data/lib/rdf/n3/algebra/list/member.rb +44 -0
- data/lib/rdf/n3/algebra/list_operator.rb +96 -0
- data/lib/rdf/n3/algebra/log/conclusion.rb +65 -0
- data/lib/rdf/n3/algebra/log/conjunction.rb +36 -0
- data/lib/rdf/n3/algebra/log/content.rb +34 -0
- data/lib/rdf/n3/algebra/log/dtlit.rb +41 -0
- data/lib/rdf/n3/algebra/log/equal_to.rb +34 -0
- data/lib/rdf/n3/algebra/log/implies.rb +102 -0
- data/lib/rdf/n3/algebra/log/includes.rb +70 -0
- data/lib/rdf/n3/algebra/log/langlit.rb +41 -0
- data/lib/rdf/n3/algebra/log/n3_string.rb +34 -0
- data/lib/rdf/n3/algebra/log/not_equal_to.rb +23 -0
- data/lib/rdf/n3/algebra/log/not_includes.rb +27 -0
- data/lib/rdf/n3/algebra/log/output_string.rb +40 -0
- data/lib/rdf/n3/algebra/log/parsed_as_n3.rb +36 -0
- data/lib/rdf/n3/algebra/log/semantics.rb +40 -0
- data/lib/rdf/n3/algebra/math/absolute_value.rb +36 -0
- data/lib/rdf/n3/algebra/math/acos.rb +26 -0
- data/lib/rdf/n3/algebra/math/acosh.rb +26 -0
- data/lib/rdf/n3/algebra/math/asin.rb +26 -0
- data/lib/rdf/n3/algebra/math/asinh.rb +26 -0
- data/lib/rdf/n3/algebra/math/atan.rb +26 -0
- data/lib/rdf/n3/algebra/math/atanh.rb +26 -0
- data/lib/rdf/n3/algebra/math/ceiling.rb +28 -0
- data/lib/rdf/n3/algebra/math/cos.rb +40 -0
- data/lib/rdf/n3/algebra/math/cosh.rb +38 -0
- data/lib/rdf/n3/algebra/math/difference.rb +40 -0
- data/lib/rdf/n3/algebra/math/equal_to.rb +54 -0
- data/lib/rdf/n3/algebra/math/exponentiation.rb +35 -0
- data/lib/rdf/n3/algebra/math/floor.rb +28 -0
- data/lib/rdf/n3/algebra/math/greater_than.rb +41 -0
- data/lib/rdf/n3/algebra/math/less_than.rb +41 -0
- data/lib/rdf/n3/algebra/math/negation.rb +38 -0
- data/lib/rdf/n3/algebra/math/not_equal_to.rb +25 -0
- data/lib/rdf/n3/algebra/math/not_greater_than.rb +25 -0
- data/lib/rdf/n3/algebra/math/not_less_than.rb +25 -0
- data/lib/rdf/n3/algebra/math/product.rb +20 -0
- data/lib/rdf/n3/algebra/math/quotient.rb +36 -0
- data/lib/rdf/n3/algebra/math/remainder.rb +35 -0
- data/lib/rdf/n3/algebra/math/rounded.rb +26 -0
- data/lib/rdf/n3/algebra/math/sin.rb +40 -0
- data/lib/rdf/n3/algebra/math/sinh.rb +38 -0
- data/lib/rdf/n3/algebra/math/sum.rb +40 -0
- data/lib/rdf/n3/algebra/math/tan.rb +40 -0
- data/lib/rdf/n3/algebra/math/tanh.rb +38 -0
- data/lib/rdf/n3/algebra/not_implemented.rb +13 -0
- data/lib/rdf/n3/algebra/resource_operator.rb +122 -0
- data/lib/rdf/n3/algebra/str/concatenation.rb +27 -0
- data/lib/rdf/n3/algebra/str/contains.rb +33 -0
- data/lib/rdf/n3/algebra/str/contains_ignoring_case.rb +33 -0
- data/lib/rdf/n3/algebra/str/ends_with.rb +33 -0
- data/lib/rdf/n3/algebra/str/equal_ignoring_case.rb +34 -0
- data/lib/rdf/n3/algebra/str/format.rb +17 -0
- data/lib/rdf/n3/algebra/str/greater_than.rb +38 -0
- data/lib/rdf/n3/algebra/str/less_than.rb +33 -0
- data/lib/rdf/n3/algebra/str/matches.rb +37 -0
- data/lib/rdf/n3/algebra/str/not_equal_ignoring_case.rb +17 -0
- data/lib/rdf/n3/algebra/str/not_greater_than.rb +17 -0
- data/lib/rdf/n3/algebra/str/not_less_than.rb +17 -0
- data/lib/rdf/n3/algebra/str/not_matches.rb +18 -0
- data/lib/rdf/n3/algebra/str/replace.rb +35 -0
- data/lib/rdf/n3/algebra/str/scrape.rb +35 -0
- data/lib/rdf/n3/algebra/str/starts_with.rb +33 -0
- data/lib/rdf/n3/algebra/time/day.rb +35 -0
- data/lib/rdf/n3/algebra/time/day_of_week.rb +27 -0
- data/lib/rdf/n3/algebra/time/gm_time.rb +29 -0
- data/lib/rdf/n3/algebra/time/hour.rb +35 -0
- data/lib/rdf/n3/algebra/time/in_seconds.rb +59 -0
- data/lib/rdf/n3/algebra/time/local_time.rb +29 -0
- data/lib/rdf/n3/algebra/time/minute.rb +35 -0
- data/lib/rdf/n3/algebra/time/month.rb +35 -0
- data/lib/rdf/n3/algebra/time/second.rb +35 -0
- data/lib/rdf/n3/algebra/time/timezone.rb +36 -0
- data/lib/rdf/n3/algebra/time/year.rb +29 -0
- data/lib/rdf/n3/algebra.rb +210 -0
- data/lib/rdf/n3/extensions.rb +221 -0
- data/lib/rdf/n3/format.rb +66 -1
- data/lib/rdf/n3/list.rb +630 -0
- data/lib/rdf/n3/reader.rb +774 -497
- data/lib/rdf/n3/reasoner.rb +282 -0
- data/lib/rdf/n3/refinements.rb +178 -0
- data/lib/rdf/n3/repository.rb +332 -0
- data/lib/rdf/n3/terminals.rb +78 -0
- data/lib/rdf/n3/vocab.rb +36 -3
- data/lib/rdf/n3/writer.rb +461 -250
- data/lib/rdf/n3.rb +11 -8
- metadata +177 -49
- data/AUTHORS +0 -1
- data/History.markdown +0 -99
- data/lib/rdf/n3/patches/array_hacks.rb +0 -53
- data/lib/rdf/n3/reader/meta.rb +0 -641
- data/lib/rdf/n3/reader/parser.rb +0 -237
data/lib/rdf/n3/writer.rb
CHANGED
@@ -1,42 +1,39 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
module RDF::N3
|
3
3
|
##
|
4
|
-
# A
|
4
|
+
# A Notation-3 serialiser in Ruby
|
5
5
|
#
|
6
6
|
# Note that the natural interface is to write a whole graph at a time.
|
7
7
|
# Writing statements or Triples will create a graph to add them to
|
8
8
|
# and then serialize the graph.
|
9
9
|
#
|
10
|
-
# @example Obtaining a
|
10
|
+
# @example Obtaining a N3 writer class
|
11
11
|
# RDF::Writer.for(:n3) #=> RDF::N3::Writer
|
12
12
|
# RDF::Writer.for("etc/test.n3")
|
13
|
-
# RDF::Writer.for("etc/test.ttl")
|
14
13
|
# RDF::Writer.for(file_name: "etc/test.n3")
|
15
|
-
# RDF::Writer.for(file_name: "etc/test.ttl")
|
16
14
|
# RDF::Writer.for(file_extension: "n3")
|
17
|
-
# RDF::Writer.for(file_extension: "ttl")
|
18
15
|
# RDF::Writer.for(content_type: "text/n3")
|
19
16
|
#
|
20
|
-
# @example Serializing RDF graph into an
|
17
|
+
# @example Serializing RDF graph into an N3 file
|
21
18
|
# RDF::N3::Writer.open("etc/test.n3") do |writer|
|
22
19
|
# writer << graph
|
23
20
|
# end
|
24
21
|
#
|
25
|
-
# @example Serializing RDF statements into an
|
22
|
+
# @example Serializing RDF statements into an N3 file
|
26
23
|
# RDF::N3::Writer.open("etc/test.n3") do |writer|
|
27
24
|
# graph.each_statement do |statement|
|
28
25
|
# writer << statement
|
29
26
|
# end
|
30
27
|
# end
|
31
28
|
#
|
32
|
-
# @example Serializing RDF statements into an
|
29
|
+
# @example Serializing RDF statements into an N3 string
|
33
30
|
# RDF::N3::Writer.buffer do |writer|
|
34
31
|
# graph.each_statement do |statement|
|
35
32
|
# writer << statement
|
36
33
|
# end
|
37
34
|
# end
|
38
35
|
#
|
39
|
-
# The writer will add prefix definitions, and use them for creating @prefix definitions, and minting
|
36
|
+
# The writer will add prefix definitions, and use them for creating @prefix definitions, and minting pnames
|
40
37
|
#
|
41
38
|
# @example Creating @base and @prefix definitions in output
|
42
39
|
# RDF::N3::Writer.buffer(base_uri: "http://example.com/", prefixes: {
|
@@ -52,12 +49,17 @@ module RDF::N3
|
|
52
49
|
class Writer < RDF::Writer
|
53
50
|
format RDF::N3::Format
|
54
51
|
include RDF::Util::Logger
|
55
|
-
|
52
|
+
include Terminals
|
53
|
+
using Refinements
|
56
54
|
|
57
|
-
# @return [
|
55
|
+
# @return [RDF::Repository] Repository of statements serialized
|
56
|
+
attr_accessor :repo
|
57
|
+
|
58
|
+
# @return [RDF::Graph] Graph being serialized
|
58
59
|
attr_accessor :graph
|
59
|
-
|
60
|
-
|
60
|
+
|
61
|
+
# @return [Array<RDF::Node>] formulae names
|
62
|
+
attr_accessor :formula_names
|
61
63
|
|
62
64
|
##
|
63
65
|
# N3 Writer options
|
@@ -78,7 +80,7 @@ module RDF::N3
|
|
78
80
|
end
|
79
81
|
|
80
82
|
##
|
81
|
-
# Initializes the
|
83
|
+
# Initializes the N3 writer instance.
|
82
84
|
#
|
83
85
|
# @param [IO, File] output
|
84
86
|
# the output stream
|
@@ -105,11 +107,15 @@ module RDF::N3
|
|
105
107
|
# @yieldreturn [void]
|
106
108
|
# @yield [writer]
|
107
109
|
# @yieldparam [RDF::Writer] writer
|
108
|
-
def initialize(output = $stdout, options
|
110
|
+
def initialize(output = $stdout, **options, &block)
|
111
|
+
@repo = RDF::N3::Repository.new
|
112
|
+
@uri_to_pname = {}
|
113
|
+
@uri_to_prefix = {}
|
109
114
|
super do
|
110
|
-
|
111
|
-
|
112
|
-
|
115
|
+
if base_uri
|
116
|
+
@uri_to_prefix[base_uri.to_s.end_with?('#', '/') ? base_uri : RDF::URI("#{base_uri}#")] = nil
|
117
|
+
end
|
118
|
+
reset
|
113
119
|
if block_given?
|
114
120
|
case block.arity
|
115
121
|
when 0 then instance_eval(&block)
|
@@ -128,7 +134,19 @@ module RDF::N3
|
|
128
134
|
# @raise [NotImplementedError] unless implemented in subclass
|
129
135
|
# @abstract
|
130
136
|
def write_triple(subject, predicate, object)
|
131
|
-
|
137
|
+
repo.insert(RDF::Statement(subject, predicate, object))
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Adds a quad to be serialized
|
142
|
+
# @param [RDF::Resource] subject
|
143
|
+
# @param [RDF::URI] predicate
|
144
|
+
# @param [RDF::Value] object
|
145
|
+
# @param [RDF::Resource] graph_name
|
146
|
+
# @return [void]
|
147
|
+
def write_quad(subject, predicate, object, graph_name)
|
148
|
+
statement = RDF::Statement.new(subject, predicate, object, graph_name: graph_name)
|
149
|
+
repo.insert(statement)
|
132
150
|
end
|
133
151
|
|
134
152
|
##
|
@@ -138,28 +156,51 @@ module RDF::N3
|
|
138
156
|
# @see #write_triple
|
139
157
|
def write_epilogue
|
140
158
|
@max_depth = @options[:max_depth] || 3
|
141
|
-
@base_uri = RDF::URI(@options[:base_uri])
|
142
159
|
|
143
160
|
self.reset
|
144
161
|
|
145
|
-
log_debug
|
162
|
+
log_debug("\nserialize: repo:") {repo.size}
|
146
163
|
|
147
164
|
preprocess
|
165
|
+
|
148
166
|
start_document
|
149
167
|
|
150
|
-
|
151
|
-
|
152
|
-
|
168
|
+
@formula_names = repo.graph_names(unique: true)
|
169
|
+
|
170
|
+
with_graph(nil) do
|
171
|
+
count = 0
|
172
|
+
order_subjects.each do |subject|
|
173
|
+
unless is_done?(subject)
|
174
|
+
statement(subject, count)
|
175
|
+
count += 1
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Output any formulae not already serialized using owl:sameAs
|
180
|
+
formula_names.each do |graph_name|
|
181
|
+
next if graph_done?(graph_name)
|
182
|
+
|
183
|
+
# Add graph_name to @formulae
|
184
|
+
@formulae[graph_name] = true
|
185
|
+
|
186
|
+
log_debug {"formula(#{graph_name})"}
|
187
|
+
@output.write("\n#{indent}")
|
188
|
+
p_term(graph_name, :subject)
|
189
|
+
@output.write(" ")
|
190
|
+
predicate(RDF::OWL.sameAs)
|
191
|
+
@output.write(" ")
|
192
|
+
formula(graph_name, :graph_name)
|
193
|
+
@output.write(" .\n")
|
153
194
|
end
|
154
195
|
end
|
155
196
|
|
156
197
|
super
|
157
198
|
end
|
158
|
-
|
159
|
-
# Return a
|
199
|
+
|
200
|
+
# Return a pname for the URI, or nil. Adds namespace of pname to defined prefixes
|
160
201
|
# @param [RDF::Resource] resource
|
161
202
|
# @return [String, nil] value to use to identify URI
|
162
|
-
def
|
203
|
+
def get_pname(resource)
|
163
204
|
case resource
|
164
205
|
when RDF::Node
|
165
206
|
return options[:unique_bnodes] ? resource.to_unique_base : resource.to_base
|
@@ -169,56 +210,55 @@ module RDF::N3
|
|
169
210
|
return nil
|
170
211
|
end
|
171
212
|
|
172
|
-
log_debug {"
|
173
|
-
|
174
|
-
when @
|
175
|
-
return @
|
213
|
+
#log_debug {"get_pname(#{resource}), std?}"}
|
214
|
+
pname = case
|
215
|
+
when @uri_to_pname.key?(uri)
|
216
|
+
return @uri_to_pname[uri]
|
176
217
|
when u = @uri_to_prefix.keys.detect {|u| uri.index(u.to_s) == 0}
|
177
218
|
# Use a defined prefix
|
178
219
|
prefix = @uri_to_prefix[u]
|
179
|
-
|
180
|
-
|
181
|
-
|
220
|
+
unless u.to_s.empty?
|
221
|
+
prefix(prefix, u) unless u.to_s.empty?
|
222
|
+
#log_debug("get_pname") {"add prefix #{prefix.inspect} => #{u}"}
|
223
|
+
uri.sub(u.to_s, "#{prefix}:")
|
224
|
+
end
|
182
225
|
when @options[:standard_prefixes] && vocab = RDF::Vocabulary.each.to_a.detect {|v| uri.index(v.to_uri.to_s) == 0}
|
183
226
|
prefix = vocab.__name__.to_s.split('::').last.downcase
|
184
227
|
@uri_to_prefix[vocab.to_uri.to_s] = prefix
|
185
228
|
prefix(prefix, vocab.to_uri) # Define for output
|
186
|
-
log_debug {"
|
229
|
+
#log_debug {"get_pname: add standard prefix #{prefix.inspect} => #{vocab.to_uri}"}
|
187
230
|
uri.sub(vocab.to_uri.to_s, "#{prefix}:")
|
188
231
|
else
|
189
232
|
nil
|
190
233
|
end
|
191
|
-
|
192
|
-
# Make sure
|
193
|
-
if
|
194
|
-
md =
|
195
|
-
|
234
|
+
|
235
|
+
# Make sure pname is a valid pname
|
236
|
+
if pname
|
237
|
+
md = PNAME_LN.match(pname) || PNAME_NS.match(pname)
|
238
|
+
pname = nil unless md.to_s.length == pname.length
|
196
239
|
end
|
197
240
|
|
198
|
-
@
|
199
|
-
rescue Addressable::URI::InvalidURIError => e
|
200
|
-
raise RDF::WriterError, "Invalid URI #{resource.inspect}: #{e.message}"
|
241
|
+
@uri_to_pname[uri] = pname
|
201
242
|
end
|
202
|
-
|
243
|
+
|
203
244
|
# Take a hash from predicate uris to lists of values.
|
204
245
|
# Sort the lists of values. Return a sorted list of properties.
|
205
|
-
# @param [Hash{
|
206
|
-
# @return [Array<
|
246
|
+
# @param [Hash{RDF::Term => Array<RDF::Term>}] properties A hash of Property to Resource mappings
|
247
|
+
# @return [Array<RDF::Term>}] Ordered list of properties. Uses predicate_order.
|
207
248
|
def sort_properties(properties)
|
208
249
|
# Make sorted list of properties
|
209
250
|
prop_list = []
|
210
|
-
|
251
|
+
|
211
252
|
predicate_order.each do |prop|
|
212
|
-
next unless properties
|
213
|
-
prop_list << prop
|
253
|
+
next unless properties.key?(prop)
|
254
|
+
prop_list << prop
|
214
255
|
end
|
215
|
-
|
256
|
+
|
216
257
|
properties.keys.sort.each do |prop|
|
217
|
-
next if prop_list.include?(prop
|
218
|
-
prop_list << prop
|
258
|
+
next if prop_list.include?(prop)
|
259
|
+
prop_list << prop
|
219
260
|
end
|
220
|
-
|
221
|
-
log_debug {"sort_properties: #{prop_list.join(', ')}"}
|
261
|
+
|
222
262
|
prop_list
|
223
263
|
end
|
224
264
|
|
@@ -228,15 +268,32 @@ module RDF::N3
|
|
228
268
|
# @param [RDF::Literal, String, #to_s] literal
|
229
269
|
# @param [Hash{Symbol => Object}] options
|
230
270
|
# @return [String]
|
231
|
-
def format_literal(literal, options
|
271
|
+
def format_literal(literal, **options)
|
232
272
|
literal = literal.dup.canonicalize! if @options[:canonicalize]
|
233
273
|
case literal
|
234
274
|
when RDF::Literal
|
235
|
-
case literal.datatype
|
236
|
-
when RDF::XSD.boolean
|
237
|
-
literal.to_s
|
275
|
+
case literal.valid? ? literal.datatype : false
|
276
|
+
when RDF::XSD.boolean
|
277
|
+
%w(true false).include?(literal.value) ? literal.value : literal.canonicalize.to_s
|
278
|
+
when RDF::XSD.integer
|
279
|
+
literal.value.match?(/^[\+\-]?\d+$/) && !canonicalize? ? literal.value : literal.canonicalize.to_s
|
280
|
+
when RDF::XSD.decimal
|
281
|
+
literal.value.match?(/^[\+\-]?\d+\.\d+?$/) && !canonicalize? ?
|
282
|
+
literal.value :
|
283
|
+
literal.canonicalize.to_s
|
238
284
|
when RDF::XSD.double
|
239
|
-
literal.
|
285
|
+
if literal.nan? || literal.infinite?
|
286
|
+
quoted(literal.value) + "^^#{format_uri(literal.datatype)}"
|
287
|
+
else
|
288
|
+
in_form = case literal.value
|
289
|
+
when /[\+\-]?\d+\.\d*E[\+\-]?\d+$/i then true
|
290
|
+
when /[\+\-]?\.\d+E[\+\-]?\d+$/i then true
|
291
|
+
when /[\+\-]?\d+E[\+\-]?\d+$/i then true
|
292
|
+
else false
|
293
|
+
end && !canonicalize?
|
294
|
+
|
295
|
+
in_form ? literal.value : literal.canonicalize.to_s.sub('E', 'e')
|
296
|
+
end
|
240
297
|
else
|
241
298
|
text = quoted(literal.value)
|
242
299
|
text << "@#{literal.language}" if literal.has_language?
|
@@ -247,47 +304,47 @@ module RDF::N3
|
|
247
304
|
quoted(literal.to_s)
|
248
305
|
end
|
249
306
|
end
|
250
|
-
|
307
|
+
|
251
308
|
##
|
252
|
-
# Returns the
|
309
|
+
# Returns the N3 representation of a URI reference.
|
253
310
|
#
|
254
311
|
# @param [RDF::URI] uri
|
255
312
|
# @param [Hash{Symbol => Object}] options
|
256
313
|
# @return [String]
|
257
|
-
def format_uri(uri, options
|
258
|
-
md = relativize(
|
259
|
-
log_debug {"
|
260
|
-
md != uri.to_s ? "<#{md}>" : (
|
314
|
+
def format_uri(uri, **options)
|
315
|
+
md = uri == base_uri ? '' : uri.relativize(base_uri)
|
316
|
+
log_debug("relativize") {"#{uri.to_sxp} => <#{md.inspect}>"} if md != uri.to_s
|
317
|
+
md != uri.to_s ? "<#{md}>" : (get_pname(uri) || "<#{uri}>")
|
261
318
|
end
|
262
|
-
|
319
|
+
|
263
320
|
##
|
264
|
-
# Returns the
|
321
|
+
# Returns the N3 representation of a blank node.
|
265
322
|
#
|
266
323
|
# @param [RDF::Node] node
|
267
324
|
# @param [Hash{Symbol => Object}] options
|
268
325
|
# @return [String]
|
269
|
-
def format_node(node, options
|
270
|
-
|
326
|
+
def format_node(node, **options)
|
327
|
+
if node.id.match(/^([^_]+)_[^_]+_([^_]+)$/)
|
328
|
+
sn, seq = $1, $2.to_i
|
329
|
+
seq = nil if seq == 0
|
330
|
+
"_:#{sn}#{seq}"
|
331
|
+
elsif options[:unique_bnodes]
|
332
|
+
node.to_unique_base
|
333
|
+
else
|
334
|
+
node.to_base
|
335
|
+
end
|
271
336
|
end
|
272
|
-
|
337
|
+
|
273
338
|
protected
|
274
339
|
# Output @base and @prefix definitions
|
275
340
|
def start_document
|
276
|
-
@output.write("
|
277
|
-
|
278
|
-
log_debug
|
341
|
+
@output.write("@base <#{base_uri}> .\n") unless base_uri.to_s.empty?
|
342
|
+
|
343
|
+
log_debug("start_document: prefixes") { prefixes.inspect}
|
279
344
|
prefixes.keys.sort_by(&:to_s).each do |prefix|
|
280
|
-
@output.write("
|
345
|
+
@output.write("@prefix #{prefix}: <#{prefixes[prefix]}> .\n")
|
281
346
|
end
|
282
347
|
end
|
283
|
-
|
284
|
-
# If base_uri is defined, use it to try to make uri relative
|
285
|
-
# @param [#to_s] uri
|
286
|
-
# @return [String]
|
287
|
-
def relativize(uri)
|
288
|
-
uri = uri.to_s
|
289
|
-
base_uri ? uri.sub(base_uri.to_s, "") : uri
|
290
|
-
end
|
291
348
|
|
292
349
|
# Defines rdf:type of subjects to be emitted at the beginning of the graph. Defaults to rdfs:Class
|
293
350
|
# @return [Array<URI>]
|
@@ -296,8 +353,18 @@ module RDF::N3
|
|
296
353
|
# Defines order of predicates to to emit at begninning of a resource description. Defaults to
|
297
354
|
# [rdf:type, rdfs:label, dc:title]
|
298
355
|
# @return [Array<URI>]
|
299
|
-
def predicate_order
|
300
|
-
|
356
|
+
def predicate_order
|
357
|
+
[
|
358
|
+
RDF.type,
|
359
|
+
RDF::RDFS.label,
|
360
|
+
RDF::RDFS.comment,
|
361
|
+
RDF::URI("http://purl.org/dc/terms/title"),
|
362
|
+
RDF::URI("http://purl.org/dc/terms/description"),
|
363
|
+
RDF::OWL.sameAs,
|
364
|
+
RDF::N3::Log.implies
|
365
|
+
]
|
366
|
+
end
|
367
|
+
|
301
368
|
# Order subjects for output. Override this to output subjects in another order.
|
302
369
|
#
|
303
370
|
# Uses #top_classes and #base_uri.
|
@@ -305,46 +372,46 @@ module RDF::N3
|
|
305
372
|
def order_subjects
|
306
373
|
seen = {}
|
307
374
|
subjects = []
|
308
|
-
|
375
|
+
|
309
376
|
# Start with base_uri
|
310
|
-
if base_uri && @subjects.keys.include?(base_uri)
|
377
|
+
if base_uri && @subjects.keys.select(&:uri?).include?(base_uri)
|
311
378
|
subjects << base_uri
|
312
379
|
seen[base_uri] = true
|
313
380
|
end
|
314
|
-
|
315
|
-
|
381
|
+
|
316
382
|
# Add distinguished classes
|
317
383
|
top_classes.each do |class_uri|
|
318
|
-
graph.query(predicate:
|
319
|
-
map {|st| st.subject}.
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
384
|
+
graph.query({predicate: RDF.type, object: class_uri}).
|
385
|
+
map {|st| st.subject}.sort.uniq.each do |subject|
|
386
|
+
log_debug("order_subjects") {subject.to_sxp}
|
387
|
+
subjects << subject
|
388
|
+
seen[subject] = true
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
# Add formulae which are subjects in this graph
|
393
|
+
@formulae.each_key do |bn|
|
394
|
+
next unless @subjects.key?(bn)
|
395
|
+
subjects << bn
|
396
|
+
seen[bn] = true
|
327
397
|
end
|
328
|
-
log_debug {"subjects2: #{subjects.inspect}"}
|
329
398
|
|
330
399
|
# Mark as seen lists that are part of another list
|
331
|
-
@lists.values.
|
332
|
-
|
333
|
-
|
334
|
-
|
400
|
+
@lists.values.flatten.each do |v|
|
401
|
+
seen[v] = true if @lists.key?(v)
|
402
|
+
end
|
403
|
+
|
404
|
+
list_elements = [] # Lists may be top-level elements
|
335
405
|
|
336
406
|
# Sort subjects by resources over bnodes, ref_counts and the subject URI itself
|
337
|
-
recursable = @subjects.keys.
|
407
|
+
recursable = (@subjects.keys - list_elements).
|
338
408
|
select {|s| !seen.include?(s)}.
|
339
409
|
map {|r| [r.node? ? 1 : 0, ref_count(r), r]}.
|
340
410
|
sort
|
341
|
-
|
342
|
-
log_debug {"subjects3: #{subjects.inspect}"}
|
411
|
+
|
343
412
|
subjects += recursable.map{|r| r.last}
|
344
|
-
log_debug {"subjects4: #{subjects.inspect}"}
|
345
|
-
subjects
|
346
413
|
end
|
347
|
-
|
414
|
+
|
348
415
|
# Perform any preprocessing of statements required
|
349
416
|
def preprocess
|
350
417
|
# Load defined prefixes
|
@@ -355,57 +422,46 @@ module RDF::N3
|
|
355
422
|
|
356
423
|
prefix(nil, @options[:default_namespace]) if @options[:default_namespace]
|
357
424
|
|
358
|
-
@
|
425
|
+
@options[:prefixes] = {} # Will define actual used when matched
|
426
|
+
repo.each {|statement| preprocess_statement(statement)}
|
359
427
|
end
|
360
|
-
|
428
|
+
|
361
429
|
# Perform any statement preprocessing required. This is used to perform reference counts and determine required
|
362
430
|
# prefixes.
|
363
431
|
# @param [Statement] statement
|
364
432
|
def preprocess_statement(statement)
|
365
|
-
#log_debug
|
366
|
-
references = ref_count(statement.object) + 1
|
367
|
-
@references[statement.object] = references
|
368
|
-
@subjects[statement.subject] = true
|
369
|
-
|
370
|
-
# Collect lists
|
371
|
-
if statement.predicate == RDF.first
|
372
|
-
@lists[statement.subject] = RDF::List.new(subject: statement.subject, graph: graph)
|
373
|
-
end
|
433
|
+
#log_debug("preprocess") {statement.inspect}
|
374
434
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
# Pre-fetch qnames, to fill prefixes
|
381
|
-
get_qname(statement.subject)
|
382
|
-
get_qname(statement.predicate)
|
383
|
-
get_qname(statement.object)
|
384
|
-
get_qname(statement.object.datatype) if statement.object.literal? && statement.object.datatype
|
385
|
-
|
386
|
-
@references[statement.predicate] = ref_count(statement.predicate) + 1
|
435
|
+
# Pre-fetch pnames, to fill prefixes
|
436
|
+
get_pname(statement.subject)
|
437
|
+
get_pname(statement.predicate)
|
438
|
+
get_pname(statement.object)
|
439
|
+
get_pname(statement.object.datatype) if statement.object.literal? && statement.object.datatype
|
387
440
|
end
|
388
|
-
|
389
|
-
#
|
390
|
-
# @
|
391
|
-
def
|
392
|
-
|
441
|
+
|
442
|
+
# Perform graph-specific preprocessing
|
443
|
+
# @param [Statement] statement
|
444
|
+
def preprocess_graph_statement(statement)
|
445
|
+
bump_reference(statement.object)
|
446
|
+
# Count properties of this subject
|
447
|
+
@subjects[statement.subject] ||= {}
|
448
|
+
@subjects[statement.subject][statement.predicate] ||= 0
|
449
|
+
@subjects[statement.subject][statement.predicate] += 1
|
393
450
|
end
|
394
451
|
|
395
452
|
# Returns indent string multiplied by the depth
|
396
453
|
# @param [Integer] modifier Increase depth by specified amount
|
397
454
|
# @return [String] A number of spaces, depending on current depth
|
398
455
|
def indent(modifier = 0)
|
399
|
-
" " * (@
|
456
|
+
" " * (@options.fetch(:log_depth, log_depth) * 2 + modifier)
|
400
457
|
end
|
401
458
|
|
402
459
|
# Reset internal helper instance variables
|
403
460
|
def reset
|
404
|
-
@depth = 0
|
405
461
|
@lists = {}
|
406
|
-
|
407
462
|
@references = {}
|
408
463
|
@serialized = {}
|
464
|
+
@graphs = {}
|
409
465
|
@subjects = {}
|
410
466
|
end
|
411
467
|
|
@@ -416,7 +472,7 @@ module RDF::N3
|
|
416
472
|
# @return [String]
|
417
473
|
def quoted(string)
|
418
474
|
if string.to_s.match(/[\t\n\r]/)
|
419
|
-
string = string.gsub('\\', '\\\\\\\\').gsub('"""', '\\"""')
|
475
|
+
string = string.gsub('\\', '\\\\\\\\').gsub('"""', '\\"\\"\\"')
|
420
476
|
%("""#{string}""")
|
421
477
|
else
|
422
478
|
"\"#{escaped(string)}\""
|
@@ -424,154 +480,309 @@ module RDF::N3
|
|
424
480
|
end
|
425
481
|
|
426
482
|
private
|
427
|
-
|
483
|
+
|
428
484
|
# Checks if l is a valid RDF list, i.e. no nodes have other properties.
|
429
|
-
def
|
430
|
-
|
431
|
-
return @lists[l] && @lists[l].valid?
|
432
|
-
end
|
433
|
-
|
434
|
-
def do_list(l)
|
435
|
-
list = @lists[l]
|
436
|
-
log_debug {"do_list: #{list.inspect}"}
|
437
|
-
position = :subject
|
438
|
-
list.each_statement do |st|
|
439
|
-
next unless st.predicate == RDF.first
|
440
|
-
log_debug {" list this: #{st.subject} first: #{st.object}[#{position}]"}
|
441
|
-
path(st.object, position)
|
442
|
-
subject_done(st.subject)
|
443
|
-
position = :object
|
444
|
-
end
|
485
|
+
def collection?(l)
|
486
|
+
return @lists.key?(l) || l.list?
|
445
487
|
end
|
446
|
-
|
447
|
-
def p_list(node, position)
|
448
|
-
return false if !is_valid_list(node)
|
449
|
-
#log_debug {"p_list: #{node.inspect}, #{position}"}
|
450
488
|
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
489
|
+
def collection(node, position)
|
490
|
+
return false if !collection?(node)
|
491
|
+
log_debug("collection") do
|
492
|
+
"#{node.to_sxp}, " +
|
493
|
+
"pos: #{position}, " +
|
494
|
+
"rc: #{ref_count(node)}"
|
495
|
+
end
|
496
|
+
|
497
|
+
@output.write("(")
|
498
|
+
log_depth do
|
499
|
+
list = node.list? ? node : @lists[node]
|
500
|
+
log_debug("collection") {list.inspect}
|
501
|
+
subject_done(RDF.nil)
|
502
|
+
subject_done(node)
|
503
|
+
index = 0
|
504
|
+
list.each do |li|
|
505
|
+
log_debug("(list first)") {"#{li}[#{position}]"}
|
506
|
+
@output.write(" ") if index > 0
|
507
|
+
path(li, :object)
|
508
|
+
subject_done(li)
|
509
|
+
index += 1
|
510
|
+
end
|
511
|
+
end
|
455
512
|
@output.write(')')
|
456
513
|
end
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
subject_done(node)
|
469
|
-
@output.write(position == :subject ? '[' : ' [')
|
470
|
-
@depth += 2
|
471
|
-
predicate_list(node)
|
472
|
-
@depth -= 2
|
473
|
-
@output.write(']')
|
474
|
-
|
475
|
-
true
|
476
|
-
end
|
477
|
-
|
478
|
-
def p_default(node, position)
|
479
|
-
#log_debug {"p_default: #{node.inspect}, #{position}"}
|
480
|
-
l = (position == :subject ? "" : " ") + format_term(node, options)
|
514
|
+
|
515
|
+
# Default singular resource representation.
|
516
|
+
def p_term(resource, position)
|
517
|
+
#log_debug("p_term") {"#{resource.to_sxp}, #{position}"}
|
518
|
+
l = if resource.is_a?(RDF::Query::Variable)
|
519
|
+
"?#{resource.name}"
|
520
|
+
elsif resource == RDF.nil
|
521
|
+
"()"
|
522
|
+
else
|
523
|
+
format_term(resource, **options)
|
524
|
+
end
|
481
525
|
@output.write(l)
|
482
526
|
end
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
527
|
+
|
528
|
+
# Represent a resource in subject, predicate or object position.
|
529
|
+
# Use either collection, blankNodePropertyList or singular resource notation.
|
530
|
+
def path(resource, position)
|
531
|
+
log_debug("path") do
|
532
|
+
"#{resource.to_sxp}, " +
|
487
533
|
"pos: #{position}, " +
|
488
|
-
"
|
489
|
-
"
|
490
|
-
"
|
534
|
+
"{}?: #{formula?(resource, position).inspect}, " +
|
535
|
+
"()?: #{collection?(resource).inspect}, " +
|
536
|
+
"[]?: #{blankNodePropertyList?(resource, position).inspect}, " +
|
537
|
+
"rc: #{ref_count(resource)}"
|
491
538
|
end
|
492
|
-
raise RDF::WriterError, "Cannot serialize
|
539
|
+
raise RDF::WriterError, "Cannot serialize resource '#{resource}'" unless
|
540
|
+
formula(resource, position) ||
|
541
|
+
collection(resource, position) ||
|
542
|
+
blankNodePropertyList(resource, position) ||
|
543
|
+
p_term(resource, position)
|
493
544
|
end
|
494
|
-
|
495
|
-
def
|
496
|
-
log_debug
|
497
|
-
|
498
|
-
|
545
|
+
|
546
|
+
def predicate(resource)
|
547
|
+
log_debug("predicate") {resource.to_sxp}
|
548
|
+
case resource
|
549
|
+
when RDF.type
|
550
|
+
@output.write("a")
|
551
|
+
when RDF::OWL.sameAs
|
552
|
+
@output.write("=")
|
553
|
+
when RDF::N3::Log.implies
|
554
|
+
@output.write("=>")
|
499
555
|
else
|
500
|
-
path(
|
556
|
+
log_depth {path(resource, :predicate)}
|
501
557
|
end
|
502
558
|
end
|
503
|
-
|
504
|
-
|
505
|
-
|
559
|
+
|
560
|
+
# Render an objectList having a common subject and predicate
|
561
|
+
def objectList(objects)
|
562
|
+
log_debug("objectList") {objects.inspect}
|
506
563
|
return if objects.empty?
|
507
564
|
|
508
|
-
|
509
|
-
|
510
|
-
|
565
|
+
log_depth do
|
566
|
+
objects.each_with_index do |obj, i|
|
567
|
+
if i > 0 && (formula?(obj, :object) || blankNodePropertyList?(obj, :object))
|
568
|
+
@output.write ", "
|
569
|
+
elsif i > 0
|
570
|
+
@output.write ",\n#{indent(4)}"
|
571
|
+
end
|
572
|
+
path(obj, :object)
|
573
|
+
end
|
511
574
|
end
|
512
575
|
end
|
513
|
-
|
514
|
-
|
576
|
+
|
577
|
+
# Render a predicateObjectList having a common subject.
|
578
|
+
# @return [Integer] the number of properties serialized
|
579
|
+
def predicateObjectList(subject, from_bpl = false)
|
515
580
|
properties = {}
|
516
|
-
@graph.
|
517
|
-
properties[st.predicate
|
518
|
-
properties[st.predicate.to_s] << st.object
|
581
|
+
@graph.enum_statement.select {|s| s.subject.sameTerm?(subject)}.each do |st|
|
582
|
+
(properties[st.predicate] ||= []) << st.object
|
519
583
|
end
|
520
584
|
|
521
585
|
prop_list = sort_properties(properties)
|
522
|
-
prop_list -= [RDF.first
|
523
|
-
log_debug {"
|
524
|
-
return if prop_list.empty?
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
586
|
+
prop_list -= [RDF.first, RDF.rest] if @lists.key?(subject)
|
587
|
+
log_debug("predicateObjectList") { "subject: #{subject.to_sxp}, properties: #{prop_list.join(', ')}" }
|
588
|
+
return 0 if prop_list.empty?
|
589
|
+
|
590
|
+
@output.write("\n#{indent(2)}") if properties.keys.length > 1 && from_bpl
|
591
|
+
log_depth do
|
592
|
+
prop_list.each_with_index do |prop, i|
|
593
|
+
begin
|
594
|
+
@output.write(";\n#{indent(2)}") if i > 0
|
595
|
+
predicate(prop)
|
596
|
+
@output.write(" ")
|
597
|
+
objectList(properties[prop])
|
598
|
+
end
|
533
599
|
end
|
534
600
|
end
|
601
|
+
properties.keys.length
|
602
|
+
end
|
603
|
+
|
604
|
+
# Can subject be represented as a blankNodePropertyList?
|
605
|
+
def blankNodePropertyList?(resource, position)
|
606
|
+
resource.node? &&
|
607
|
+
!formula?(resource, position) &&
|
608
|
+
!collection?(resource) &&
|
609
|
+
(!is_done?(resource) || position == :subject) &&
|
610
|
+
ref_count(resource) == (position == :object ? 1 : 0) &&
|
611
|
+
!repo.has_graph?(resource)
|
612
|
+
end
|
613
|
+
|
614
|
+
def blankNodePropertyList(resource, position)
|
615
|
+
return false unless blankNodePropertyList?(resource, position)
|
616
|
+
|
617
|
+
log_debug("blankNodePropertyList") {resource.to_sxp}
|
618
|
+
subject_done(resource)
|
619
|
+
@output.write((position == :subject ? "\n#{indent}[" : '['))
|
620
|
+
num_props = log_depth {predicateObjectList(resource, true)}
|
621
|
+
@output.write((num_props > 1 ? "\n#{indent(2)}" : "") + (position == :subject ? '] .' : ']'))
|
622
|
+
true
|
535
623
|
end
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
624
|
+
|
625
|
+
# Can subject be represented as a formula?
|
626
|
+
def formula?(resource, position)
|
627
|
+
!!@formulae[resource]
|
628
|
+
end
|
629
|
+
|
630
|
+
def formula(resource, position)
|
631
|
+
return false unless formula?(resource, position)
|
632
|
+
|
633
|
+
log_debug("formula") {resource.to_sxp}
|
634
|
+
subject_done(resource)
|
635
|
+
@output.write('{')
|
636
|
+
count = 0
|
637
|
+
log_depth do
|
638
|
+
with_graph(resource) do
|
639
|
+
order_subjects.each do |subject|
|
640
|
+
unless is_done?(subject)
|
641
|
+
statement(subject, count)
|
642
|
+
count += 1
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
end
|
647
|
+
@output.write((count > 0 ? "#{indent}" : "") + '}')
|
550
648
|
true
|
551
649
|
end
|
552
|
-
|
553
|
-
|
650
|
+
|
651
|
+
# Render triples having the same subject using an explicit subject
|
652
|
+
def triples(subject)
|
554
653
|
@output.write("\n#{indent}")
|
555
654
|
path(subject, :subject)
|
556
|
-
|
557
|
-
|
655
|
+
@output.write(" ")
|
656
|
+
num_props = predicateObjectList(subject)
|
657
|
+
@output.puts("#{num_props > 0 ? ' ' : ''}.")
|
558
658
|
true
|
559
659
|
end
|
560
|
-
|
561
|
-
def statement(subject)
|
562
|
-
log_debug
|
660
|
+
|
661
|
+
def statement(subject, count)
|
662
|
+
log_debug("statement") do
|
663
|
+
"#{subject.to_sxp}, " +
|
664
|
+
"{}?: #{formula?(subject, :subject).inspect}, " +
|
665
|
+
"()?: #{collection?(subject).inspect}, " +
|
666
|
+
"[]?: #{blankNodePropertyList?(subject, :subject).inspect}, "
|
667
|
+
end
|
563
668
|
subject_done(subject)
|
564
|
-
|
565
|
-
@output.
|
669
|
+
blankNodePropertyList(subject, :subject) || triples(subject)
|
670
|
+
@output.puts if count > 0 || graph.graph_name
|
671
|
+
end
|
672
|
+
|
673
|
+
# Return the number of times this node has been referenced in the object position
|
674
|
+
# @return [Integer]
|
675
|
+
def ref_count(node)
|
676
|
+
@references.fetch(node, 0)
|
566
677
|
end
|
567
|
-
|
678
|
+
|
679
|
+
# Increase the reference count of this resource
|
680
|
+
# @param [RDF::Resource] resource
|
681
|
+
# @return [Integer] resulting reference count
|
682
|
+
def bump_reference(resource)
|
683
|
+
@references[resource] = ref_count(resource) + 1
|
684
|
+
end
|
685
|
+
|
568
686
|
def is_done?(subject)
|
569
687
|
@serialized.include?(subject)
|
570
688
|
end
|
571
|
-
|
689
|
+
|
572
690
|
# Mark a subject as done.
|
573
691
|
def subject_done(subject)
|
574
692
|
@serialized[subject] = true
|
575
693
|
end
|
694
|
+
|
695
|
+
def graph_done?(subject)
|
696
|
+
@graphs.include?(subject)
|
697
|
+
end
|
698
|
+
|
699
|
+
# Mark a graph as done.
|
700
|
+
def graph_done(graph_name)
|
701
|
+
@graphs[graph_name] = true
|
702
|
+
end
|
703
|
+
|
704
|
+
# Process a graph projection
|
705
|
+
def with_graph(graph_name)
|
706
|
+
old_lists, @lists = @lists, {}
|
707
|
+
old_references, @references = @references, {}
|
708
|
+
old_serialized, @serialized = @serialized, {}
|
709
|
+
old_subjects, @subjects = @subjects, {}
|
710
|
+
old_graph, @graph = @graph, repo.project_graph(graph_name)
|
711
|
+
old_formulae, @formulae = @formulae, {}
|
712
|
+
|
713
|
+
graph_done(graph_name)
|
714
|
+
|
715
|
+
lists = {}
|
716
|
+
graph.each do |statement|
|
717
|
+
preprocess_graph_statement(statement)
|
718
|
+
[statement.subject, statement.object].each do |resource|
|
719
|
+
@formulae[resource] = true if
|
720
|
+
resource.node? &&
|
721
|
+
(formula_names.include?(resource) || resource.id.start_with?('_form_'))
|
722
|
+
|
723
|
+
# First-class list may have members which are formulae, and need reference counts
|
724
|
+
if resource.list?
|
725
|
+
resource.each_descendant do |term|
|
726
|
+
bump_reference(term)
|
727
|
+
@formulae[term] = true if
|
728
|
+
term.node? &&
|
729
|
+
(formula_names.include?(term) || term.id.start_with?('_form_'))
|
730
|
+
end
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
# Collect list elements
|
735
|
+
if [RDF.first, RDF.rest].include?(statement.predicate) && statement.subject.node?
|
736
|
+
lists[statement.subject] ||= {}
|
737
|
+
lists[statement.subject][statement.predicate] = statement.object
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
# Remove list entries after head with more than two properties (other than rdf:type)
|
742
|
+
rests = lists.values.map {|props| props[RDF.rest]}
|
743
|
+
|
744
|
+
# Remove non-head lists that have too many properties
|
745
|
+
rests.select do |bn|
|
746
|
+
pc = 0
|
747
|
+
@subjects.fetch(bn, {}).each do |pred, count|
|
748
|
+
next if pred == RDF.type
|
749
|
+
pc += count
|
750
|
+
end
|
751
|
+
lists.delete(bn) if pc > 2
|
752
|
+
end
|
753
|
+
|
754
|
+
# Values for this list element, recursive
|
755
|
+
def list_values(bn, lists)
|
756
|
+
raise "no list" unless lists.has_key?(bn)
|
757
|
+
first, rest = lists[bn][RDF.first], lists[bn][RDF.rest]
|
758
|
+
(rest == RDF.nil ? [] : list_values(rest, lists)).unshift(first)
|
759
|
+
rescue
|
760
|
+
lists.delete(bn)
|
761
|
+
raise $!
|
762
|
+
end
|
763
|
+
|
764
|
+
# Create value arrays for each entry
|
765
|
+
lists.each do |bn, props|
|
766
|
+
begin
|
767
|
+
@lists[bn] = list_values(bn, lists)
|
768
|
+
rescue
|
769
|
+
# Skip this list element, if it raises an exception
|
770
|
+
lists.delete(bn)
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
# Mark all remaining rests done
|
775
|
+
rests.each {|bn| subject_done(bn) if lists.include?(bn)}
|
776
|
+
|
777
|
+
# Remove entries that are referenced as rdf:rest of some entry
|
778
|
+
lists.each do |bn, props|
|
779
|
+
@lists.delete(props[RDF.rest])
|
780
|
+
end
|
781
|
+
|
782
|
+
# Record nodes in subject or object
|
783
|
+
yield
|
784
|
+
ensure
|
785
|
+
@graph, @lists, @references, @serialized, @subjects, @formulae = old_graph, old_lists, old_references, old_serialized, old_subjects, old_formulae
|
786
|
+
end
|
576
787
|
end
|
577
788
|
end
|