rdf-n3 0.0.1 → 0.0.2

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.
File without changes
data/lib/rdf/n3/writer.rb CHANGED
@@ -0,0 +1,444 @@
1
+ require 'rdf/rdfxml/patches/graph_properties'
2
+
3
+ module RDF::N3
4
+ ##
5
+ # A Turtle serialiser in Ruby
6
+ #
7
+ # Note that the natural interface is to write a whole graph at a time.
8
+ # Writing statements or Triples will create a graph to add them to
9
+ # and then serialize the graph.
10
+ #
11
+ # @example Obtaining a Turtle writer class
12
+ # RDF::Writer.for(:n3) #=> RDF::N3::Writer
13
+ # RDF::Writer.for("etc/test.n3")
14
+ # RDF::Writer.for("etc/test.ttl")
15
+ # RDF::Writer.for(:file_name => "etc/test.n3")
16
+ # RDF::Writer.for(:file_name => "etc/test.ttl")
17
+ # RDF::Writer.for(:file_extension => "n3")
18
+ # RDF::Writer.for(:file_extension => "ttl")
19
+ # RDF::Writer.for(:content_type => "text/n3")
20
+ # RDF::Writer.for(:content_type => "text/turtle")
21
+ #
22
+ # @example Serializing RDF graph into an Turtle file
23
+ # RDF::N3::Write.open("etc/test.n3") do |writer|
24
+ # writer << graph
25
+ # end
26
+ #
27
+ # @example Serializing RDF statements into an Turtle file
28
+ # RDF::N3::Writer.open("etc/test.n3") do |writer|
29
+ # graph.each_statement do |statement|
30
+ # writer << statement
31
+ # end
32
+ # end
33
+ #
34
+ # @example Serializing RDF statements into an Turtle string
35
+ # RDF::N3::Writer.buffer do |writer|
36
+ # graph.each_statement do |statement|
37
+ # writer << statement
38
+ # end
39
+ # end
40
+ #
41
+ # @author [Gregg Kellogg](http://kellogg-assoc.com/)
42
+ class Writer < RDF::Writer
43
+ format RDF::N3::Format
44
+
45
+ SUBJECT = 0
46
+ VERB = 1
47
+ OBJECT = 2
48
+
49
+ attr_accessor :graph, :base_uri
50
+
51
+ ##
52
+ # Initializes the Turtle writer instance.
53
+ #
54
+ # Opitons:
55
+ # max_depth:: Maximum depth for recursively defining resources, defaults to 3
56
+ # base_uri:: Base URI of graph, used to shorting URI references
57
+ # default_namespace:: URI to use as default namespace
58
+ #
59
+ # @param [IO, File] output
60
+ # @param [Hash{Symbol => Object}] options
61
+ # @option options [Integer] :max_depth (nil)
62
+ # @option options [String, #to_s] :base_uri (nil)
63
+ # @option options [String, #to_s] :lang (nil)
64
+ # @option options [Array] :attributes (nil)
65
+ # @option options [String] :default_namespace
66
+ # @yield [writer]
67
+ # @yieldparam [RDF::Writer] writer
68
+ def initialize(output = $stdout, options = {}, &block)
69
+ @graph = RDF::Graph.new
70
+ @stream = output
71
+ super
72
+ end
73
+
74
+ ##
75
+ # @param [Graph] graph
76
+ # @return [void]
77
+ def insert_graph(graph)
78
+ @graph = graph
79
+ end
80
+
81
+ ##
82
+ # @param [Statement] statement
83
+ # @return [void]
84
+ def insert_statement(statement)
85
+ @graph << statement
86
+ end
87
+
88
+ ##
89
+ # Stores the RDF/XML representation of a triple.
90
+ #
91
+ # @param [RDF::Resource] subject
92
+ # @param [RDF::URI] predicate
93
+ # @param [RDF::Value] object
94
+ # @return [void]
95
+ # @see #write_epilogue
96
+ def insert_triple(subject, predicate, object)
97
+ @graph << RDF::Statement.new(subject, predicate, object)
98
+ end
99
+
100
+ ##
101
+ # Outputs the N3 representation of all stored triples.
102
+ #
103
+ # @return [void]
104
+ # @see #write_triple
105
+ def write_epilogue
106
+ @max_depth = @options[:max_depth] || 3
107
+ @base = @options[:base_uri]
108
+ @debug = @options[:debug]
109
+ @default_namespace = @options[:default_namespace]
110
+
111
+ self.reset
112
+
113
+ add_debug "\nserialize: graph: #{@graph.size}"
114
+
115
+ add_namespace("", @default_namespace) if @default_namespace
116
+
117
+ preprocess
118
+ start_document
119
+
120
+ order_subjects.each do |subject|
121
+ #puts "subj: #{subject.inspect}"
122
+ unless is_done?(subject)
123
+ statement(subject)
124
+ end
125
+ end
126
+ end
127
+
128
+ protected
129
+ def start_document
130
+ @started = true
131
+
132
+ write("#{indent}@base <#{@base}> .\n") if @base
133
+
134
+ add_debug("start_document: #{@namespaces.inspect}")
135
+ @namespaces.keys.sort.each do |prefix|
136
+ write("#{indent}@prefix #{prefix}: <#{@namespaces[prefix]}> .\n")
137
+ end
138
+ end
139
+
140
+ def end_document; end
141
+
142
+ # Checks if l is a valid RDF list, i.e. no nodes have other properties.
143
+ def is_valid_list(l)
144
+ props = @graph.properties(l)
145
+ #puts "is_valid_list: #{props.inspect}" if $DEBUG
146
+ return false unless props.has_key?(RDF.first.to_s) || l == RDF.nil
147
+ while l && l != RDF.nil do
148
+ #puts "is_valid_list(length): #{props.length}" if $DEBUG
149
+ return false unless props.has_key?(RDF.first.to_s) && props.has_key?(RDF.rest.to_s)
150
+ n = props[RDF.rest.to_s]
151
+ #puts "is_valid_list(n): #{n.inspect}" if $DEBUG
152
+ return false unless n.is_a?(Array) && n.length == 1
153
+ l = n.first
154
+ props = @graph.properties(l)
155
+ end
156
+ #puts "is_valid_list: valid" if $DEBUG
157
+ true
158
+ end
159
+
160
+ def do_list(l)
161
+ puts "do_list: #{l.inspect}" if $DEBUG
162
+ position = SUBJECT
163
+ while l do
164
+ p = @graph.properties(l)
165
+ item = p.fetch(RDF.first.to_s, []).first
166
+ if item
167
+ path(item, position)
168
+ subject_done(l)
169
+ position = OBJECT
170
+ end
171
+ l = p.fetch(RDF.rest.to_s, []).first
172
+ end
173
+ end
174
+
175
+ def p_list(node, position)
176
+ return false if !is_valid_list(node)
177
+ #puts "p_list: #{node.inspect}, #{position}" if $DEBUG
178
+
179
+ write(position == SUBJECT ? "(" : " (")
180
+ @depth += 2
181
+ do_list(node)
182
+ @depth -= 2
183
+ write(')')
184
+ end
185
+
186
+ def p_squared?(node, position)
187
+ node.is_a?(RDF::Node) &&
188
+ !@serialized.has_key?(node) &&
189
+ ref_count(node) <= 1
190
+ end
191
+
192
+ def p_squared(node, position)
193
+ return false unless p_squared?(node, position)
194
+
195
+ #puts "p_squared: #{node.inspect}, #{position}" if $DEBUG
196
+ subject_done(node)
197
+ write(position == SUBJECT ? '[' : ' [')
198
+ @depth += 2
199
+ predicate_list(node)
200
+ @depth -= 2
201
+ write(']')
202
+
203
+ true
204
+ end
205
+
206
+ def p_default(node, position)
207
+ #puts "p_default: #{node.inspect}, #{position}" if $DEBUG
208
+ l = (position == SUBJECT ? "" : " ") + label(node)
209
+ write(l)
210
+ end
211
+
212
+ def path(node, position)
213
+ puts "path: #{node.inspect}, pos: #{position}, []: #{is_valid_list(node)}, p2?: #{p_squared?(node, position)}, rc: #{ref_count(node)}" if $DEBUG
214
+ raise RDF::WriterError, "Cannot serialize node '#{node}'" unless p_list(node, position) || p_squared(node, position) || p_default(node, position)
215
+ end
216
+
217
+ def verb(node)
218
+ puts "verb: #{node.inspect}" if $DEBUG
219
+ if node == RDF.type
220
+ write(" a")
221
+ else
222
+ path(node, VERB)
223
+ end
224
+ end
225
+
226
+ def object_list(objects)
227
+ puts "object_list: #{objects.inspect}" if $DEBUG
228
+ return if objects.empty?
229
+
230
+ objects.each_with_index do |obj, i|
231
+ write(",\n#{indent(2)}") if i > 0
232
+ path(obj, OBJECT)
233
+ end
234
+ end
235
+
236
+ def predicate_list(subject)
237
+ properties = @graph.properties(subject)
238
+ prop_list = sort_properties(properties) - [RDF.first.to_s, RDF.rest.to_s]
239
+ puts "predicate_list: #{prop_list.inspect}" if $DEBUG
240
+ return if prop_list.empty?
241
+
242
+ prop_list.each_with_index do |prop, i|
243
+ write(";\n#{indent(2)}") if i > 0
244
+ verb(RDF::URI.new(prop))
245
+ object_list(properties[prop])
246
+ end
247
+ end
248
+
249
+ def s_squared?(subject)
250
+ ref_count(subject) == 0 && subject.is_a?(RDF::Node) && !is_valid_list(subject)
251
+ end
252
+
253
+ def s_squared(subject)
254
+ return false unless s_squared?(subject)
255
+
256
+ write("\n#{indent} [")
257
+ @depth += 1
258
+ predicate_list(subject)
259
+ @depth -= 1
260
+ write("] .")
261
+ true
262
+ end
263
+
264
+ def s_default(subject)
265
+ write("\n#{indent}")
266
+ path(subject, SUBJECT)
267
+ predicate_list(subject)
268
+ write(" .")
269
+ true
270
+ end
271
+
272
+ def relativize(uri)
273
+ uri = uri.to_s
274
+ @base ? uri.sub(/^#{@base}/, "") : uri
275
+ end
276
+
277
+ def statement(subject)
278
+ puts "statement: #{subject.inspect}, s2?: #{s_squared(subject)}" if $DEBUG
279
+ subject_done(subject)
280
+ s_squared(subject) || s_default(subject)
281
+ end
282
+
283
+ MAX_DEPTH = 10
284
+ INDENT_STRING = " "
285
+
286
+ def top_classes; [RDF::RDFS.Class]; end
287
+ def predicate_order; [RDF.type, RDF::RDFS.label, RDF::DC.title]; end
288
+
289
+ def is_done?(subject)
290
+ @serialized.include?(subject)
291
+ end
292
+
293
+ # Mark a subject as done.
294
+ def subject_done(subject)
295
+ @serialized[subject] = true
296
+ end
297
+
298
+ def order_subjects
299
+ seen = {}
300
+ subjects = []
301
+
302
+ top_classes.each do |class_uri|
303
+ graph.query(:predicate => RDF.type, :object => class_uri).map {|st| st.subject}.sort.uniq.each do |subject|
304
+ #add_debug "order_subjects: #{subject.inspect}"
305
+ subjects << subject
306
+ seen[subject] = @top_levels[subject] = true
307
+ end
308
+ end
309
+
310
+ # Sort subjects by resources over bnodes, ref_counts and the subject URI itself
311
+ recursable = @subjects.keys.
312
+ select {|s| !seen.include?(s)}.
313
+ map {|r| [r.is_a?(RDF::Node) ? 1 : 0, ref_count(r), r]}.
314
+ sort
315
+
316
+ subjects += recursable.map{|r| r.last}
317
+ end
318
+
319
+ def preprocess
320
+ @graph.each {|statement| preprocess_statement(statement)}
321
+ end
322
+
323
+ def preprocess_statement(statement)
324
+ #add_debug "preprocess: #{statement.inspect}"
325
+ references = ref_count(statement.object) + 1
326
+ @references[statement.object] = references
327
+ @subjects[statement.subject] = true
328
+
329
+ # Pre-fetch qnames, to fill namespaces
330
+ get_qname(statement.subject)
331
+ get_qname(statement.predicate)
332
+ get_qname(statement.object)
333
+
334
+ @references[statement.predicate] = ref_count(statement.predicate) + 1
335
+ end
336
+
337
+ # Return the number of times this node has been referenced in the object position
338
+ def ref_count(node)
339
+ @references.fetch(node, 0)
340
+ end
341
+
342
+ # Return a QName for the URI, or nil. Adds namespace of QName to defined namespaces
343
+ def get_qname(uri)
344
+ if uri.is_a?(RDF::URI)
345
+ md = uri.to_s.match(/^#{@base}(.*)$/) if @base
346
+ return "<#{md[1]}>" if md
347
+
348
+ # Duplicate logic from URI#qname to remember namespace assigned
349
+
350
+ if uri.qname
351
+ return ":#{uri.qname.last}" if uri.vocab == @default_namespace
352
+ add_namespace(uri.qname.first, uri.vocab)
353
+ add_debug "get_qname(uri.qname): #{uri.qname.join(':')}"
354
+ return uri.qname.join(":")
355
+ end
356
+
357
+ # No vocabulary assigned, find one from cache of created namespace URIs
358
+ @namespaces.each_pair do |prefix, vocab|
359
+ if uri.to_s.index(vocab.to_s) == 0
360
+ uri.vocab = vocab
361
+ local_name = uri.to_s[(vocab.to_s.length)..-1]
362
+ if vocab == @default_namespace
363
+ add_debug "get_qname(ns): :#{local_name}"
364
+ return ":#{local_name}"
365
+ else
366
+ add_debug "get_qname(ns): #{prefix}:#{local_name}"
367
+ return "#{prefix}:#{local_name}"
368
+ end
369
+ end
370
+ end
371
+
372
+ nil
373
+ end
374
+ end
375
+
376
+ def label(node)
377
+ get_qname(node) || (node.uri? ? "<#{node}>" : node.to_s)
378
+ end
379
+
380
+ def add_namespace(prefix, ns)
381
+ return if @namespaces.has_key?(prefix.to_s)
382
+ add_debug "add_namespace: '#{prefix}', <#{ns}>"
383
+ @namespaces[prefix.to_s] = ns.to_s
384
+ end
385
+
386
+ def reset
387
+ @depth = 0
388
+ @lists = {}
389
+ @namespaces = {}
390
+ @references = {}
391
+ @serialized = {}
392
+ @subjects = {}
393
+ @top_levels = {}
394
+ @shortNames = {}
395
+ @started = false
396
+ end
397
+
398
+ # Take a hash from predicate uris to lists of values.
399
+ # Sort the lists of values. Return a sorted list of properties.
400
+ def sort_properties(properties)
401
+ properties.keys.each do |k|
402
+ properties[k] = properties[k].sort do |a, b|
403
+ a_li = a.is_a?(RDF::URI) && a.qname && a.qname.last =~ /^_\d+$/ ? a.to_i : a.to_s
404
+ b_li = b.is_a?(RDF::URI) && b.qname && b.qname.last =~ /^_\d+$/ ? b.to_i : b.to_s
405
+
406
+ a_li <=> b_li
407
+ end
408
+ end
409
+
410
+ # Make sorted list of properties
411
+ prop_list = []
412
+
413
+ predicate_order.each do |prop|
414
+ next unless properties[prop]
415
+ prop_list << prop.to_s
416
+ end
417
+
418
+ properties.keys.sort.each do |prop|
419
+ next if prop_list.include?(prop.to_s)
420
+ prop_list << prop.to_s
421
+ end
422
+
423
+ add_debug "sort_properties: #{prop_list.to_sentence}"
424
+ prop_list
425
+ end
426
+
427
+ # Add debug event to debug array, if specified
428
+ #
429
+ # @param [String] message::
430
+ def add_debug(message)
431
+ @debug << message if @debug.is_a?(Array)
432
+ end
433
+
434
+ # Returns indent string multiplied by the depth
435
+ def indent(modifier = 0)
436
+ INDENT_STRING * (@depth + modifier)
437
+ end
438
+
439
+ # Write text
440
+ def write(text)
441
+ @stream.write(text)
442
+ end
443
+ end
444
+ end
data/rdf-n3.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{rdf-n3}
8
- s.version = "0.0.1"
8
+ s.version = "0.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Gregg Kellogg"]
12
- s.date = %q{2010-06-03}
12
+ s.date = %q{2010-06-24}
13
13
  s.description = %q{ RDF::N3 is an Notation-3 (n3-rdf) parser for Ruby using the RDF.rb library suite.
14
14
  }
15
15
  s.email = %q{gregg@kellogg-assoc.com}
@@ -30,15 +30,26 @@ Gem::Specification.new do |s|
30
30
  "lib/rdf/n3.rb",
31
31
  "lib/rdf/n3/format.rb",
32
32
  "lib/rdf/n3/patches/array_hacks.rb",
33
+ "lib/rdf/n3/patches/graph_properties.rb",
34
+ "lib/rdf/n3/patches/literal_hacks.rb",
35
+ "lib/rdf/n3/patches/literal_normalization.rb",
36
+ "lib/rdf/n3/patches/qname_hacks.rb",
33
37
  "lib/rdf/n3/patches/rdf_escape.rb",
38
+ "lib/rdf/n3/patches/seq.rb",
39
+ "lib/rdf/n3/patches/uri_hacks.rb",
34
40
  "lib/rdf/n3/reader.rb",
35
41
  "lib/rdf/n3/reader/n3_grammar.rb",
36
42
  "lib/rdf/n3/reader/n3_grammar.treetop",
37
43
  "lib/rdf/n3/version.rb",
38
- "lib/rdf/n3/vocabulary.rb",
44
+ "lib/rdf/n3/vocab.rb",
39
45
  "lib/rdf/n3/writer.rb",
40
46
  "rdf-n3.gemspec",
47
+ "script/console",
48
+ "script/parse",
41
49
  "spec/cwm_spec.rb",
50
+ "spec/format_spec.rb",
51
+ "spec/literal_spec.rb",
52
+ "spec/matchers.rb",
42
53
  "spec/n3reader_spec.rb",
43
54
  "spec/rdf_helper.rb",
44
55
  "spec/rdfcore/Manifest.rdf",
@@ -395,7 +406,6 @@ Gem::Specification.new do |s|
395
406
  "spec/rdfcore/xmlsch-02/test003.rdf",
396
407
  "spec/spec.opts",
397
408
  "spec/spec_helper.rb",
398
- "spec/swap_helper.rb",
399
409
  "spec/swap_spec.rb",
400
410
  "spec/swap_test/.DS_Store",
401
411
  "spec/swap_test/animal.rdf",
@@ -430,6 +440,7 @@ Gem::Specification.new do |s|
430
440
  "spec/swap_test/n3/n3parser.tests_n3_10019.nt",
431
441
  "spec/swap_test/n3/n3parser.tests_n3_10020.nt",
432
442
  "spec/swap_test/n3parser.tests",
443
+ "spec/swap_test/n3parser.yml",
433
444
  "spec/swap_test/nodeID/classes.n3",
434
445
  "spec/swap_test/nodeID/classes.ref.rdf",
435
446
  "spec/swap_test/nodeID/ex1.rdf",
@@ -469,6 +480,7 @@ Gem::Specification.new do |s|
469
480
  "spec/swap_test/ref/xml-syntax-basic-serialization.rdf",
470
481
  "spec/swap_test/ref/xmllit.nt",
471
482
  "spec/swap_test/regression.n3",
483
+ "spec/swap_test/regression.yml",
472
484
  "spec/swap_test/reluri-1.n3",
473
485
  "spec/swap_test/strquot.n3",
474
486
  "spec/swap_test/syntax/colon-in-uri.rdf",
@@ -505,7 +517,6 @@ Gem::Specification.new do |s|
505
517
  "spec/swap_test/xml-syntax/xml_prefix2.n3",
506
518
  "spec/swap_test/xml-syntax/xmlbase3.rdf",
507
519
  "spec/swap_test/xml-syntax/xmllit.rdf",
508
- "spec/triple_spec.rb",
509
520
  "spec/turtle/README.txt",
510
521
  "spec/turtle/bad-00.ttl",
511
522
  "spec/turtle/bad-01.ttl",
@@ -594,7 +605,8 @@ Gem::Specification.new do |s|
594
605
  "spec/turtle/test-30.out",
595
606
  "spec/turtle/test-30.ttl",
596
607
  "spec/turtle_serializer_spec.rb",
597
- "spec/turtle_spec.rb"
608
+ "spec/turtle_spec.rb",
609
+ "spec/writer_spec.rb"
598
610
  ]
599
611
  s.homepage = %q{http://github.com/gkellogg/rdf-rdfa}
600
612
  s.rdoc_options = ["--charset=UTF-8"]
@@ -603,14 +615,16 @@ Gem::Specification.new do |s|
603
615
  s.summary = %q{Notation-3 (n3-rdf) and Turtle reader/writer for RDF.rb.}
604
616
  s.test_files = [
605
617
  "spec/cwm_spec.rb",
618
+ "spec/format_spec.rb",
619
+ "spec/literal_spec.rb",
620
+ "spec/matchers.rb",
606
621
  "spec/n3reader_spec.rb",
607
622
  "spec/rdf_helper.rb",
608
623
  "spec/spec_helper.rb",
609
- "spec/swap_helper.rb",
610
624
  "spec/swap_spec.rb",
611
- "spec/triple_spec.rb",
612
625
  "spec/turtle_serializer_spec.rb",
613
- "spec/turtle_spec.rb"
626
+ "spec/turtle_spec.rb",
627
+ "spec/writer_spec.rb"
614
628
  ]
615
629
 
616
630
  if s.respond_to? :specification_version then
@@ -618,21 +632,30 @@ Gem::Specification.new do |s|
618
632
  s.specification_version = 3
619
633
 
620
634
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
621
- s.add_runtime_dependency(%q<rdf>, [">= 0.1.6"])
635
+ s.add_runtime_dependency(%q<rdf>, [">= 0.2.0"])
636
+ s.add_runtime_dependency(%q<treetop>, [">= 1.4.0"])
622
637
  s.add_development_dependency(%q<rspec>, [">= 0"])
623
638
  s.add_development_dependency(%q<rdf-spec>, [">= 0"])
624
- s.add_development_dependency(%q<treetop>, [">= 1.4.0"])
639
+ s.add_development_dependency(%q<rdf-rdfxml>, [">= 0.2.0"])
640
+ s.add_development_dependency(%q<rdf-isomorphic>, [">= 0"])
641
+ s.add_development_dependency(%q<yard>, [">= 0"])
625
642
  else
626
- s.add_dependency(%q<rdf>, [">= 0.1.6"])
643
+ s.add_dependency(%q<rdf>, [">= 0.2.0"])
644
+ s.add_dependency(%q<treetop>, [">= 1.4.0"])
627
645
  s.add_dependency(%q<rspec>, [">= 0"])
628
646
  s.add_dependency(%q<rdf-spec>, [">= 0"])
629
- s.add_dependency(%q<treetop>, [">= 1.4.0"])
647
+ s.add_dependency(%q<rdf-rdfxml>, [">= 0.2.0"])
648
+ s.add_dependency(%q<rdf-isomorphic>, [">= 0"])
649
+ s.add_dependency(%q<yard>, [">= 0"])
630
650
  end
631
651
  else
632
- s.add_dependency(%q<rdf>, [">= 0.1.6"])
652
+ s.add_dependency(%q<rdf>, [">= 0.2.0"])
653
+ s.add_dependency(%q<treetop>, [">= 1.4.0"])
633
654
  s.add_dependency(%q<rspec>, [">= 0"])
634
655
  s.add_dependency(%q<rdf-spec>, [">= 0"])
635
- s.add_dependency(%q<treetop>, [">= 1.4.0"])
656
+ s.add_dependency(%q<rdf-rdfxml>, [">= 0.2.0"])
657
+ s.add_dependency(%q<rdf-isomorphic>, [">= 0"])
658
+ s.add_dependency(%q<yard>, [">= 0"])
636
659
  end
637
660
  end
638
661