rdf-rdfxml 0.2.3 → 0.3.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.
@@ -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