rdf-n3 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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