rdf-rdfxml 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,8 @@
1
1
  module RDF::RDFXML::VERSION
2
- MAJOR = 0
3
- MINOR = 2
4
- TINY = 3
5
- EXTRA = nil
2
+ VERSION_FILE = File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "..", "VERSION")
3
+ MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chop.split(".")
6
4
 
7
- STRING = [MAJOR, MINOR, TINY].join('.')
8
- STRING << ".#{EXTRA}" if EXTRA
5
+ STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
9
6
 
10
7
  ##
11
8
  # @return [String]
@@ -17,5 +14,5 @@ module RDF::RDFXML::VERSION
17
14
 
18
15
  ##
19
16
  # @return [Array(Integer, Integer, Integer)]
20
- def self.to_a() [MAJOR, MINOR, TINY] end
17
+ def self.to_a() STRING.split(".") end
21
18
  end
@@ -9,6 +9,8 @@ module RDF::RDFXML
9
9
  # Writing statements or Triples will create a graph to add them to
10
10
  # and then serialize the graph.
11
11
  #
12
+ # The writer will add prefix definitions, and use them for creating @prefix definitions, and minting QNames
13
+ #
12
14
  # @example Obtaining a RDF/XML writer class
13
15
  # RDF::Writer.for(:rdf) #=> RDF::RDFXML::Writer
14
16
  # RDF::Writer.for("etc/test.rdf")
@@ -35,62 +37,88 @@ module RDF::RDFXML
35
37
  # end
36
38
  # end
37
39
  #
40
+ # @example Creating @base and @prefix definitions in output
41
+ # RDF::RDFXML::Writer.buffer(:base_uri => "http://example.com/", :prefixes => {
42
+ # nil => "http://example.com/ns#",
43
+ # :foaf => "http://xmlns.com/foaf/0.1/"}
44
+ # ) do |writer|
45
+ # graph.each_statement do |statement|
46
+ # writer << statement
47
+ # end
48
+ # end
49
+ #
38
50
  # @author [Gregg Kellogg](http://kellogg-assoc.com/)
39
51
  class Writer < RDF::Writer
40
52
  format RDF::RDFXML::Format
41
53
 
42
54
  VALID_ATTRIBUTES = [:none, :untyped, :typed]
43
55
 
44
- attr_accessor :graph, :base_uri
45
-
56
+ # @return [Graph] Graph of statements serialized
57
+ attr_accessor :graph
58
+ # @return [URI] Base URI used for relativizing URIs
59
+ attr_accessor :base_uri
60
+
46
61
  ##
47
62
  # Initializes the RDF/XML writer instance.
48
63
  #
49
- # Opitons:
50
- # max_depth:: Maximum depth for recursively defining resources, defaults to 3
51
- # base_uri:: Base URI of graph, used to shorting URI references
52
- # lang:: Output as root xml:lang attribute, and avoid generation _xml:lang_ where possible
53
- # attributes:: How to use XML attributes when serializing, one of :none, :untyped, :typed. The default is :none.
54
- # default_namespace:: URI to use as default namespace
55
- #
56
- # @param [IO, File] output
64
+ # @param [IO, File] output
65
+ # the output stream
57
66
  # @param [Hash{Symbol => Object}] options
58
- # @option options [Integer] :max_depth (nil)
59
- # @option options [String, #to_s] :base_uri (nil)
60
- # @option options [String, #to_s] :lang (nil)
61
- # @option options [Array] :attributes (nil)
62
- # @option options [String] :default_namespace
67
+ # any additional options
68
+ # @option options [Boolean] :canonicalize (false)
69
+ # whether to canonicalize literals when serializing
70
+ # @option options [Hash] :prefixes (Hash.new)
71
+ # the prefix mappings to use (not supported by all writers)
72
+ # @option options [#to_s] :base_uri (nil)
73
+ # the base URI to use when constructing relative URIs
74
+ # @option options [Integer] :max_depth (3)
75
+ # Maximum depth for recursively defining resources
76
+ # @option options [#to_s] :lang (nil)
77
+ # Output as root xml:lang attribute, and avoid generation _xml:lang_ where possible
78
+ # @option options [Array] :attributes (nil)
79
+ # How to use XML attributes when serializing, one of :none, :untyped, :typed. The default is :none.
80
+ # @option options [Boolean] :standard_prefixes (false)
81
+ # Add standard prefixes to _prefixes_, if necessary.
82
+ # @option options [String] :default_namespace (nil)
83
+ # URI to use as default namespace, same as prefix(nil)
63
84
  # @yield [writer]
64
85
  # @yieldparam [RDF::Writer] writer
65
86
  def initialize(output = $stdout, options = {}, &block)
66
- @graph = RDF::Graph.new
67
- super
87
+ super do
88
+ @graph = RDF::Graph.new
89
+ @uri_to_qname = {}
90
+ prefix(nil, @options[:default_namespace]) if @options[:default_namespace]
91
+ block.call(self) if block_given?
92
+ end
68
93
  end
69
94
 
70
95
  ##
96
+ # Write whole graph
97
+ #
71
98
  # @param [Graph] graph
72
99
  # @return [void]
73
- def insert_graph(graph)
100
+ def write_graph(graph)
74
101
  @graph = graph
75
102
  end
76
103
 
77
104
  ##
78
- # @param [Statement] statement
105
+ # Addes a statement to be serialized
106
+ # @param [RDF::Statement] statement
79
107
  # @return [void]
80
- def insert_statement(statement)
81
- @graph << statement
108
+ def write_statement(statement)
109
+ @graph.insert(statement)
82
110
  end
83
111
 
84
112
  ##
85
- # Stores the RDF/XML representation of a triple.
86
- #
113
+ # Addes a triple to be serialized
87
114
  # @param [RDF::Resource] subject
88
115
  # @param [RDF::URI] predicate
89
116
  # @param [RDF::Value] object
90
117
  # @return [void]
91
- # @see #write_epilogue
92
- def insert_triple(subject, predicate, object)
93
- @graph << RDF::Statement.new(subject, predicate, object)
118
+ # @raise [NotImplementedError] unless implemented in subclass
119
+ # @abstract
120
+ def write_triple(subject, predicate, object)
121
+ @graph.insert(Statement.new(subject, predicate, object))
94
122
  end
95
123
 
96
124
  ##
@@ -99,14 +127,12 @@ module RDF::RDFXML
99
127
  # @return [void]
100
128
  # @see #write_triple
101
129
  def write_epilogue
102
- @base_uri = nil
103
130
  @force_RDF_about = {}
104
131
  @max_depth = @options[:max_depth] || 3
105
132
  @base_uri = @options[:base_uri]
106
133
  @lang = @options[:lang]
107
134
  @attributes = @options[:attributes] || :none
108
135
  @debug = @options[:debug]
109
- @default_namespace = @options[:default_namespace]
110
136
  raise "Invalid attribute option '#{@attributes}', should be one of #{VALID_ATTRIBUTES.to_sentence}" unless VALID_ATTRIBUTES.include?(@attributes.to_sym)
111
137
  self.reset
112
138
 
@@ -116,28 +142,12 @@ module RDF::RDFXML
116
142
 
117
143
  preprocess
118
144
 
119
- # Get QNames and necessary namespaces from predicates and objects
120
- @graph.predicates.each {|pred| add_debug("serialize pred: #{pred.inspect}"); get_qname(pred)}
121
- @graph.objects.each {|obj| add_debug("serialize obj: #{obj.inspect}"); get_qname(obj)}
122
145
  prefix(:rdf, RDF.to_uri)
123
146
  prefix(:xml, RDF::XML) if @base_uri || @lang
124
147
 
125
- if @default_namespace
126
- prefix(:__default__, @default_namespace.respond_to?(:to_uri) ? @default_namespace.to_uri : @default_namespace)
127
- @default_namespace_prefix = prefixes.invert[@default_namespace]
128
- add_debug("def_namespace: #{@default_namespace}, prefix: #{@default_namespace_prefix}")
129
- end
130
-
131
148
  add_debug "\nserialize: graph namespaces: #{prefixes.inspect}"
132
149
 
133
150
  doc.root = Nokogiri::XML::Element.new("rdf:RDF", doc)
134
- prefixes.each_pair do |p, uri|
135
- if p == :__default__
136
- doc.root.default_namespace = uri.to_s
137
- else
138
- doc.root.add_namespace(p.to_s, uri.to_s)
139
- end
140
- end
141
151
  doc.root["xml:lang"] = @lang if @lang
142
152
  doc.root["xml:base"] = @base_uri if @base_uri
143
153
 
@@ -147,38 +157,176 @@ module RDF::RDFXML
147
157
  subject(subject, doc.root)
148
158
  end
149
159
 
160
+ prefixes.each_pair do |p, uri|
161
+ if p == nil
162
+ doc.root.default_namespace = uri.to_s
163
+ else
164
+ doc.root.add_namespace(p.to_s, uri.to_s)
165
+ end
166
+ end
167
+
150
168
  doc.write_xml_to(@output, :encoding => "UTF-8", :indent => 2)
151
169
  end
152
170
 
171
+ # Return a QName for the URI, or nil. Adds namespace of QName to defined prefixes
172
+ # @param [URI,#to_s] uri
173
+ # @return [Array<Symbol,Symbol>, nil] Prefix, Suffix pair or nil, if none found
174
+ def get_qname(uri)
175
+ uri = RDF::URI.intern(uri.to_s) unless uri.is_a?(URI)
176
+
177
+ unless @uri_to_qname.has_key?(uri)
178
+ # Find in defined prefixes
179
+ prefixes.each_pair do |prefix, vocab|
180
+ if uri.to_s.index(vocab.to_s) == 0
181
+ local_name = uri.to_s[(vocab.to_s.length)..-1]
182
+ add_debug "get_qname(ns): #{prefix}:#{local_name}"
183
+ return @uri_to_qname[uri] = [prefix, local_name.to_sym]
184
+ end
185
+ end
186
+
187
+ # Use a default vocabulary
188
+ if @options[:standard_prefixes] && vocab = RDF::Vocabulary.detect {|v| uri.to_s.index(v.to_uri.to_s) == 0}
189
+ prefix = vocab.__name__.to_s.split('::').last.downcase
190
+ prefixes[prefix.to_sym] = vocab.to_uri
191
+ suffix = uri.to_s[vocab.to_uri.to_s.size..-1]
192
+ return @uri_to_qname[uri] = [prefix.to_sym, suffix.empty? ? nil : suffix.to_sym] if prefix && suffix
193
+ end
194
+
195
+ # No vocabulary found, invent one
196
+ # Add bindings for predicates not already having bindings
197
+ # From RDF/XML Syntax and Processing:
198
+ # An XML namespace-qualified name (QName) has restrictions on the legal characters such that not all
199
+ # property URIs can be expressed as these names. It is recommended that implementors of RDF serializers,
200
+ # in order to break a URI into a namespace name and a local name, split it after the last XML non-NCName
201
+ # character, ensuring that the first character of the name is a Letter or '_'. If the URI ends in a
202
+ # non-NCName character then throw a "this graph cannot be serialized in RDF/XML" exception or error.
203
+ separation = uri.to_s.rindex(%r{[^a-zA-Z_0-9-][a-zA-Z_][a-z0-9A-Z_-]*$})
204
+ return @uri_to_qname[uri] = nil unless separation
205
+ base_uri = uri.to_s[0..separation]
206
+ suffix = uri.to_s[separation+1..-1]
207
+ @gen_prefix = @gen_prefix ? @gen_prefix.succ : "ns0"
208
+ add_debug "create prefix definition for #{uri}"
209
+ prefix(@gen_prefix, base_uri)
210
+ add_debug "get_qname(tmp_ns): #{@gen_prefix}:#{suffix}"
211
+ return @uri_to_qname[uri] = [@gen_prefix.to_sym, suffix.to_sym]
212
+ end
213
+
214
+ @uri_to_qname[uri]
215
+ rescue Addressable::URI::InvalidURIError
216
+ @uri_to_qname[uri] = nil
217
+ end
218
+
153
219
  protected
220
+ # If @base_uri is defined, use it to try to make uri relative
221
+ # @param [#to_s] uri
222
+ # @return [String]
223
+ def relativize(uri)
224
+ uri = uri.to_s
225
+ @base_uri ? uri.sub(@base_uri.to_s, "") : uri
226
+ end
227
+
228
+ # Defines rdf:type of subjects to be emitted at the beginning of the graph. Defaults to rdfs:Class
229
+ # @return [Array<URI>]
230
+ def top_classes; [RDF::RDFS.Class]; end
231
+
232
+ # Defines order of predicates to to emit at begninning of a resource description. Defaults to
233
+ # [rdf:type, rdfs:label, dc:title]
234
+ # @return [Array<URI>]
235
+ def predicate_order; [RDF.type, RDF::RDFS.label, RDF::DC.title]; end
236
+
237
+ # Order subjects for output. Override this to output subjects in another order.
238
+ #
239
+ # Uses top_classes
240
+ # @return [Array<Resource>] Ordered list of subjects
241
+ def order_subjects
242
+ seen = {}
243
+ subjects = []
244
+
245
+ top_classes.each do |class_uri|
246
+ graph.query(:predicate => RDF.type, :object => class_uri).map {|st| st.subject}.sort.uniq.each do |subject|
247
+ #add_debug "order_subjects: #{subject.inspect}"
248
+ subjects << subject
249
+ seen[subject] = @top_levels[subject] = true
250
+ end
251
+ end
252
+
253
+ # Sort subjects by resources over bnodes, ref_counts and the subject URI itself
254
+ recursable = @subjects.keys.
255
+ select {|s| !seen.include?(s)}.
256
+ map {|r| [r.is_a?(RDF::Node) ? 1 : 0, ref_count(r), r]}.
257
+ sort
258
+
259
+ subjects += recursable.map{|r| r.last}
260
+ end
261
+
262
+ # Perform any preprocessing of statements required
263
+ def preprocess
264
+ @graph.each {|statement| preprocess_statement(statement)}
265
+ end
266
+
267
+ # Perform any statement preprocessing required. This is used to perform reference counts and determine required
268
+ # prefixes.
269
+ # @param [Statement] statement
270
+ def preprocess_statement(statement)
271
+ #add_debug "preprocess: #{statement.inspect}"
272
+ references = ref_count(statement.object) + 1
273
+ @references[statement.object] = references
274
+ @subjects[statement.subject] = true
275
+ end
276
+
277
+ # Returns indent string multiplied by the depth
278
+ # @param [Integer] modifier Increase depth by specified amount
279
+ # @return [String] A number of spaces, depending on current depth
280
+ def indent(modifier = 0)
281
+ " " * (@depth + modifier)
282
+ end
283
+
284
+ def reset
285
+ @depth = 0
286
+ @lists = {}
287
+ prefixes = {}
288
+ @references = {}
289
+ @serialized = {}
290
+ @subjects = {}
291
+ @top_levels = {}
292
+ end
293
+
294
+ private
154
295
  def subject(subject, parent_node)
155
296
  node = nil
156
297
 
157
298
  if !is_done?(subject)
158
299
  subject_done(subject)
159
300
  properties = @graph.properties(subject)
160
- prop_list = sort_properties(properties)
161
301
  add_debug "subject: #{subject.inspect}, props: #{properties.inspect}"
162
302
 
163
303
  rdf_type, *rest = properties.fetch(RDF.type.to_s, [])
164
- if rdf_type.is_a?(RDF::URI)
165
- element = get_qname(rdf_type)
166
- properties[RDF.type.to_s] = rest
167
-
168
- # FIXME: different namespace logic
169
- type_ns = rdf_type.vocab rescue nil
170
- if type_ns && @default_namespace && type_ns.to_s == @default_namespace.to_s
171
- properties[RDF.type.to_s] = rest
172
- element = rdf_type.qname.last
173
- end
304
+ qname = get_qname_string(rdf_type, :with_default => true)
305
+ add_debug "subject: #{subject.inspect}, qname: #{qname.inspect}"
306
+ if rdf_type.is_a?(RDF::Node)
307
+ # Must serialize with an element
308
+ rdf_type = nil
309
+ elsif rest.empty?
310
+ properties.delete(RDF.type.to_s)
311
+ else
312
+ properties[RDF.type.to_s] = [rest].flatten.compact
174
313
  end
175
- element ||= "rdf:Description"
314
+ prop_list = order_properties(properties)
176
315
 
177
- node = Nokogiri::XML::Element.new(element.to_s, parent_node.document)
316
+ if qname
317
+ rdf_type = nil
318
+ else
319
+ qname = "rdf:Description"
320
+ prefixes[:rdf] = RDF.to_uri
321
+ end
322
+
323
+ node = Nokogiri::XML::Element.new(qname, parent_node.document)
324
+
325
+ node["rdf:type"] = rdf_type if rdf_type
178
326
 
179
327
  if subject.is_a?(RDF::Node)
180
328
  # Only need nodeID if it's referenced elsewhere
181
- node["rdf:nodeID"] = subject.to_s if ref_count(subject) > (@depth == 0 ? 0 : 1)
329
+ node["rdf:nodeID"] = subject.id if ref_count(subject) > (@depth == 0 ? 0 : 1)
182
330
  else
183
331
  node["rdf:about"] = relativize(subject)
184
332
  end
@@ -206,15 +354,12 @@ module RDF::RDFXML
206
354
  #
207
355
  # If _is_unique_ is true, this predicate may be able to be serialized as an attribute
208
356
  def predicate(prop, object, node, is_unique)
209
- qname = get_qname(prop)
210
- raise RDF::WriterError, "No qname generated for <#{prop}>" unless qname
211
-
212
357
  # See if we can serialize as attribute.
213
358
  # * untyped attributes that aren't duplicated where xml:lang == @lang
214
359
  # * typed attributes that aren't duplicated if @dt_as_attr is true
215
360
  # * rdf:type
216
361
  as_attr = false
217
- as_attr ||= true if [:untyped, :typed].include?(@attributes) && prop == RDF.type
362
+ as_attr = true if [:untyped, :typed].include?(@attributes) && prop == RDF.type
218
363
 
219
364
  # Untyped attribute with no lang, or whos lang is the same as the default and RDF.type
220
365
  add_debug("as_attr? #{@attributes}, plain? #{object.plain?}, lang #{@lang || 'nil'}:#{object.language || 'nil'}") if object.is_a?(RDF::Literal)
@@ -224,22 +369,15 @@ module RDF::RDFXML
224
369
  as_attr ||= true if [:typed].include?(@attributes) && object.is_a?(RDF::Literal) && object.typed?
225
370
 
226
371
  as_attr = false unless is_unique
227
-
372
+
373
+ qname = get_qname_string(prop, :with_default => !as_attr)
374
+ raise RDF::WriterError, "No qname generated for <#{prop}>" unless qname
375
+
228
376
  # Can't do as an attr if the qname has no prefix and there is no prefixed version
229
- if @default_namespace && prop.vocab.to_s == @default_namespace.to_s
230
- if as_attr
231
- if @default_namespace_prefix
232
- qname = "#{@default_namespace_prefix}:#{prop.qname.last}"
233
- else
234
- as_attr = false
235
- end
236
- else
237
- qname = prop.qname.last.to_s
238
- end
239
- end
377
+ as_attr = false if as_attr && qname !~ /:/
240
378
 
241
379
  add_debug "predicate: #{qname}, as_attr: #{as_attr}, object: #{object.inspect}, done: #{is_done?(object)}, sub: #{@subjects.include?(object)}"
242
- qname = "rdf:li" if qname.match(/rdf:_\d+/)
380
+ #qname = "rdf:li" if qname.match(/rdf:_\d+/)
243
381
  pred_node = Nokogiri::XML::Element.new(qname, node.document)
244
382
 
245
383
  if object.is_a?(RDF::Literal) || is_done?(object) || !@subjects.include?(object)
@@ -266,16 +404,15 @@ module RDF::RDFXML
266
404
  end
267
405
  add_debug " elt #{'xmllit ' if object.is_a?(RDF::Literal) && object.datatype == RDF.XMLLiteral}content=#{args.first}" if !args.empty?
268
406
  if object.is_a?(RDF::Literal) && object.datatype == RDF.XMLLiteral
269
- pred_node.add_child(Nokogiri::XML::CharacterData.new(args.first, node.document))
407
+ pred_node.inner_html = args.first.to_s
270
408
  elsif args.first
271
409
  pred_node.content = args.first unless args.empty?
272
410
  end
273
411
  end
274
412
  else
275
- require 'rdf/rdfxml/patches/seq' unless RDF::Graph.respond_to?(:seq)
276
413
 
277
414
  # Check to see if it can be serialized as a collection
278
- col = @graph.seq(object)
415
+ col = RDF::List.new(object, @graph).to_a
279
416
  conformant_list = col.all? {|item| !item.is_a?(RDF::Literal)}
280
417
  o_props = @graph.properties(object)
281
418
  if conformant_list && o_props[RDF.first.to_s]
@@ -304,130 +441,30 @@ module RDF::RDFXML
304
441
  node.add_child(pred_node) if pred_node
305
442
  end
306
443
 
307
- def relativize(uri)
308
- uri = uri.to_s
309
- self.base_uri ? uri.sub(/^#{self.base_uri}/, "") : uri
310
- end
311
-
312
- def preprocess_triple(triple)
313
- super
314
-
315
- # Pre-fetch qnames, to fill namespaces
316
- get_qname(triple.predicate)
317
- get_qname(triple.object) if triple.predicate == RDF.type
318
-
319
- @references[triple.predicate] = ref_count(triple.predicate) + 1
320
- end
321
-
322
- MAX_DEPTH = 10
323
- INDENT_STRING = " "
324
-
325
- def top_classes; [RDF::RDFS.Class]; end
326
- def predicate_order; [RDF.type, RDF::RDFS.label, RDF::DC.title]; end
327
-
328
- def is_done?(subject)
329
- @serialized.include?(subject)
330
- end
331
-
332
444
  # Mark a subject as done.
333
445
  def subject_done(subject)
334
446
  @serialized[subject] = true
335
447
  end
336
448
 
337
- def order_subjects
338
- seen = {}
339
- subjects = []
340
-
341
- top_classes.each do |class_uri|
342
- graph.query(:predicate => RDF.type, :object => class_uri).map {|st| st.subject}.sort.uniq.each do |subject|
343
- #add_debug "order_subjects: #{subject.inspect}"
344
- subjects << subject
345
- seen[subject] = @top_levels[subject] = true
346
- end
347
- end
348
-
349
- # Sort subjects by resources over bnodes, ref_counts and the subject URI itself
350
- recursable = @subjects.keys.
351
- select {|s| !seen.include?(s)}.
352
- map {|r| [r.is_a?(RDF::Node) ? 1 : 0, ref_count(r), r]}.
353
- sort
354
-
355
- subjects += recursable.map{|r| r.last}
356
- end
357
-
358
- def preprocess
359
- @graph.each {|statement| preprocess_statement(statement)}
360
- end
361
-
362
- def preprocess_statement(statement)
363
- #add_debug "preprocess: #{statement.inspect}"
364
- references = ref_count(statement.object) + 1
365
- @references[statement.object] = references
366
- @subjects[statement.subject] = true
367
- end
368
-
369
449
  # Return the number of times this node has been referenced in the object position
370
450
  def ref_count(node)
371
451
  @references.fetch(node, 0)
372
452
  end
373
453
 
374
- # Return a QName for the URI, or nil. Adds namespace of QName to defined namespaces
375
- def get_qname(uri)
376
- if uri.is_a?(RDF::URI)
377
- # Duplicate logic from URI#qname to remember namespace assigned
378
- if uri.qname
379
- prefix(uri.qname.first, uri.vocab.to_uri)
380
- add_debug "get_qname(uri.qname): #{uri.qname.join(':')}"
381
- return uri.qname.join(":")
382
- end
383
-
384
- # No vocabulary assigned, find one from cache of created namespace URIs
385
- prefixes.each_pair do |prefix, vocab|
386
- if uri.to_s.index(vocab.to_s) == 0
387
- uri.vocab = vocab
388
- local_name = uri.to_s[(vocab.to_s.length)..-1]
389
- add_debug "get_qname(ns): #{prefix}:#{local_name}"
390
- return "#{prefix}:#{local_name}"
391
- end
392
- end
393
-
394
- # No vocabulary found, invent one
395
- # Add bindings for predicates not already having bindings
396
- # From RDF/XML Syntax and Processing:
397
- # An XML namespace-qualified name (QName) has restrictions on the legal characters such that not all property URIs can be expressed
398
- # as these names. It is recommended that implementors of RDF serializers, in order to break a URI into a namespace name and a local
399
- # name, split it after the last XML non-NCName character, ensuring that the first character of the name is a Letter or '_'. If the
400
- # URI ends in a non-NCName character then throw a "this graph cannot be serialized in RDF/XML" exception or error.
401
- separation = uri.to_s.rindex(%r{[^a-zA-Z_0-9-](?=[a-zA-Z_])})
402
- return nil unless separation
403
- base_uri = uri.to_s[0..separation]
404
- local_name = uri.to_s[separation+1..-1]
405
- @tmp_ns = @tmp_ns ? @tmp_ns.succ : "ns0"
406
- add_debug "create namespace definition for #{uri}"
407
- uri.vocab = RDF::Vocabulary(base_uri)
408
- prefix(@tmp_ns.to_sym, uri.vocab.to_uri)
409
- add_debug "get_qname(tmp_ns): #{@tmp_ns}:#{local_name}"
410
- return "#{@tmp_ns}:#{local_name}"
411
- end
454
+ def is_done?(subject)
455
+ @serialized.include?(subject)
412
456
  end
413
457
 
414
- def reset
415
- @depth = 0
416
- @lists = {}
417
- prefixes = {}
418
- @references = {}
419
- @serialized = {}
420
- @subjects = {}
421
- @top_levels = {}
422
- end
423
-
458
+
424
459
  # Take a hash from predicate uris to lists of values.
425
460
  # Sort the lists of values. Return a sorted list of properties.
426
- def sort_properties(properties)
461
+ # @param [Hash{String => Array<Resource>}] properties A hash of Property to Resource mappings
462
+ # @return [Array<String>}] Ordered list of properties. Uses predicate_order.
463
+ def order_properties(properties)
427
464
  properties.keys.each do |k|
428
465
  properties[k] = properties[k].sort do |a, b|
429
- a_li = a.is_a?(RDF::URI) && a.qname && a.qname.last =~ /^_\d+$/ ? a.to_i : a.to_s
430
- b_li = b.is_a?(RDF::URI) && b.qname && b.qname.last =~ /^_\d+$/ ? b.to_i : b.to_s
466
+ a_li = a.is_a?(RDF::URI) && get_qname(a) && get_qname(a).last.to_s =~ /^_\d+$/ ? a.to_i : a.to_s
467
+ b_li = b.is_a?(RDF::URI) && get_qname(b) && get_qname(b).last.to_s =~ /^_\d+$/ ? b.to_i : b.to_s
431
468
 
432
469
  a_li <=> b_li
433
470
  end
@@ -446,7 +483,7 @@ module RDF::RDFXML
446
483
  prop_list << prop.to_s
447
484
  end
448
485
 
449
- add_debug "sort_properties: #{prop_list.to_sentence}"
486
+ add_debug "order_properties: #{prop_list.to_sentence}"
450
487
  prop_list
451
488
  end
452
489
 
@@ -475,12 +512,24 @@ module RDF::RDFXML
475
512
  #
476
513
  # @param [String] message::
477
514
  def add_debug(message)
515
+ STDERR.puts message if ::RDF::RDFXML.debug?
478
516
  @debug << message if @debug.is_a?(Array)
479
517
  end
480
518
 
481
- # Returns indent string multiplied by the depth
482
- def indent(modifier = 0)
483
- INDENT_STRING * (@depth + modifier)
519
+ # Return string representation of QName pair
520
+ #
521
+ # @option [Boolean] :with_default (false) If a default mapping exists, use it, otherwise if a prefixed mapping exists, use it
522
+ def get_qname_string(uri, options = {})
523
+ if qname = get_qname(uri)
524
+ if options[:with_default]
525
+ qname[0] = nil if !qname.first.nil? && prefix(qname.first).to_s == prefix(nil).to_s
526
+ elsif qname.first.nil?
527
+ prefix = nil
528
+ prefixes.each_pair {|k, v| prefix = k if !k.nil? && v.to_s == prefix(nil).to_s}
529
+ qname[0] = prefix if prefix
530
+ end
531
+ qname.first == nil ? qname.last.to_s : qname.map(&:to_s).join(":")
532
+ end
484
533
  end
485
534
  end
486
535
  end