rdf_context 0.5.7 → 0.5.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/History.rdoc +15 -1
  2. data/README.rdoc +2 -0
  3. data/Rakefile +2 -4
  4. data/VERSION +1 -1
  5. data/bin/rdf_context +5 -54
  6. data/lib/rdf_context.rb +5 -0
  7. data/lib/rdf_context/graph.rb +68 -49
  8. data/lib/rdf_context/n3parser.rb +2 -2
  9. data/lib/rdf_context/namespace.rb +1 -1
  10. data/lib/rdf_context/nokogiri_hacks.rb +7 -0
  11. data/lib/rdf_context/parser.rb +57 -13
  12. data/lib/rdf_context/rdfaparser.rb +200 -130
  13. data/lib/rdf_context/rdfxmlparser.rb +8 -8
  14. data/lib/rdf_context/serializer/recursive_serializer.rb +1 -1
  15. data/lib/rdf_context/serializer/turtle_serializer.rb +15 -15
  16. data/lib/rdf_context/serializer/xml_serializer.rb +8 -8
  17. data/lib/rdf_context/store/memory_store.rb +14 -14
  18. data/lib/rdf_context/store/sqlite3_store.rb +4 -4
  19. data/lib/rdf_context/uriref.rb +11 -4
  20. data/script/console +1 -3
  21. data/script/tc +44 -0
  22. data/spec/.gitignore +1 -0
  23. data/spec/aggregate_graph_spec.rb +1 -0
  24. data/spec/bnode_spec.rb +2 -1
  25. data/spec/conjunctive_graph_spec.rb +1 -0
  26. data/spec/cwm_spec.rb +1 -0
  27. data/spec/duration_spec.rb +1 -0
  28. data/spec/graph_spec.rb +27 -0
  29. data/spec/list_store_spec.rb +1 -0
  30. data/spec/literal_spec.rb +1 -0
  31. data/spec/matchers.rb +1 -1
  32. data/spec/memory_store_spec.rb +1 -0
  33. data/spec/n3parser_spec.rb +1 -0
  34. data/spec/namespaces_spec.rb +1 -0
  35. data/spec/parser_spec.rb +1 -0
  36. data/spec/rdf_helper.rb +4 -4
  37. data/spec/rdfa_helper.rb +24 -0
  38. data/spec/rdfa_parser_spec.rb +6 -36
  39. data/spec/rdfcore/.gitignore +1 -0
  40. data/spec/rdfxml_spec.rb +1 -0
  41. data/spec/sqlite3_store_spec.rb +1 -0
  42. data/spec/string_hacks_spec.rb +2 -0
  43. data/spec/swap_test/.gitignore +1 -0
  44. data/spec/triple_spec.rb +1 -0
  45. data/spec/turtle/.gitignore +1 -0
  46. data/spec/turtle_serializer_spec.rb +3 -2
  47. data/spec/turtle_spec.rb +1 -0
  48. data/spec/uriref_spec.rb +13 -12
  49. data/spec/xml_serializer_spec.rb +7 -6
  50. metadata +26 -61
  51. data/spec/html4-manifest.yml +0 -4937
  52. data/spec/html5-manifest.yml +0 -4937
  53. data/spec/rdfcore/Manifest.yml +0 -6242
  54. data/spec/swap_test/n3parser.yml +0 -773
  55. data/spec/swap_test/regression.yml +0 -902
  56. data/spec/turtle/manifest-bad.yml +0 -807
  57. data/spec/turtle/manifest.yml +0 -807
  58. data/spec/xhtml-manifest.yml +0 -3901
  59. data/spec/xhtml11-manifest.yml +0 -4405
@@ -1,4 +1,5 @@
1
1
  require File.join(File.dirname(__FILE__), 'parser')
2
+ require 'open-uri'
2
3
 
3
4
  module RdfContext
4
5
  ##
@@ -6,11 +7,24 @@ module RdfContext
6
7
  #
7
8
  # Based on processing rules described here:
8
9
  # @see http://www.w3.org/TR/rdfa-syntax/#s_model RDFa 1.0
9
- # @see http://www.w3.org/2010/02/rdfa/drafts/2010/ED-rdfa-core-20100705/ RDFa 1.1
10
+ # @see http://www.w3.org/2010/02/rdfa/drafts/2010/WD-rdfa-core-20100803/ RDFa 1.1
10
11
  #
11
12
  # @author Ben Adida
12
13
  # @author Gregg Kellogg
13
14
  class RdfaParser < Parser
15
+ SafeCURIEorCURIEorURI = {
16
+ :rdfa_1_0 => [:term, :safe_curie, :uri, :bnode],
17
+ :rdfa_1_1 => [:safe_curie, :curie, :term, :uri, :bnode],
18
+ }
19
+ TERMorCURIEorAbsURI = {
20
+ :rdfa_1_0 => [:term, :curie],
21
+ :rdfa_1_1 => [:term, :curie, :absuri],
22
+ }
23
+ TERMorCURIEorAbsURIprop = {
24
+ :rdfa_1_0 => [:curie],
25
+ :rdfa_1_1 => [:term, :curie, :absuri],
26
+ }
27
+
14
28
  # Host language
15
29
  # @return [:xhtml]
16
30
  attr_reader :host_language
@@ -83,7 +97,7 @@ module RdfContext
83
97
  # @return [URIRef]
84
98
  attr :default_vocabulary, true
85
99
 
86
- # @param [String] base
100
+ # @param [URIRef] base
87
101
  # @param [Hash] host_defaults
88
102
  # @option host_defaults [Hash{String => URIRef}] :term_mappings Hash of NCName => URIRef
89
103
  # @option host_defaults [Hash{String => Namespace}] :vocabulary Hash of prefix => URIRef
@@ -92,7 +106,7 @@ module RdfContext
92
106
  @base = base
93
107
  @parent_subject = @base
94
108
  @parent_object = nil
95
- @uri_mappings = {}
109
+ @uri_mappings = host_defaults.fetch(:uri_mappings, {})
96
110
  @incomplete_triples = []
97
111
  @language = nil
98
112
  @term_mappings = host_defaults.fetch(:term_mappings, {})
@@ -121,6 +135,7 @@ module RdfContext
121
135
  # Creates a new parser for RDFa.
122
136
  #
123
137
  # @option options [Graph] :graph (nil) Graph to parse into, otherwise a new RdfContext::Graph instance is created
138
+ # @option options [Graph] :processor_graph (nil) Graph to record information, warnings and errors.
124
139
  # @option options [Array] :debug (nil) Array to place debug messages
125
140
  # @option options [:rdfxml, :html, :n3] :type (nil)
126
141
  # @option options [Boolean] :strict (false) Raise Error if true, continue with lax parsing, otherwise
@@ -140,6 +155,8 @@ module RdfContext
140
155
  # @param [Nokogiri::HTML::Document, Nokogiri::XML::Document, #read, #to_s] stream the HTML+RDFa IO stream, string, Nokogiri::HTML::Document or Nokogiri::XML::Document
141
156
  # @param [String] uri (nil) the URI of the document
142
157
  # @option options [Array] :debug (nil) Array to place debug messages
158
+ # @option options [:rdfa_1_0, :rdfa_1_1] :version (:rdfa_1_1) Parser version information
159
+ # @option options [:xhtml] :host_language (:xhtml) Host Language
143
160
  # @option options [Boolean] :strict (false) Raise Error if true, continue with lax parsing, otherwise
144
161
  # @return [Graph] Returns the graph containing parsed triples
145
162
  # @yield [triple]
@@ -154,22 +171,19 @@ module RdfContext
154
171
  else Nokogiri::XML.parse(stream, uri.to_s)
155
172
  end
156
173
 
157
- raise ParserException, "Empty document" if @doc.nil? && @strict
174
+ add_error(nil, "Empty document", RDFA_NS.HostLanguageMarkupError) if @doc.nil?
175
+ add_warning(nil, "Synax errors:\n#{@doc.errors}", RDFA_NS.HostLanguageMarkupError) unless @doc.errors.empty?
176
+
158
177
  @callback = block
159
178
 
160
- # Determine host language
161
- # XXX - right now only XHTML defined
162
- version = @doc.root.attributes["version"].to_s if @doc.root
163
-
164
- @host_language = case version.to_s
165
- when /XHTML+RDFa/ then :xhtml
166
- end
167
-
168
- # If none found, assume xhtml
169
- @host_language ||= :xhtml
170
-
171
- @version = version.to_s.match(/RDFa 1.0/) ? :rdfa_1_0 : :rdfa_1_1
179
+ @version = options[:version] ? options[:version].to_sym : :rdfa_1_1
180
+ @host_language = options[:host_language] || :xhtml
172
181
 
182
+ # Section 4.2 RDFa Host Language Conformance
183
+ #
184
+ # The Host Language may define a default RDFa Profile. If it does, the RDFa Profile triples that establish term or
185
+ # URI mappings associated with that profile must not change without changing the profile URI. RDFa Processors may
186
+ # embed, cache, or retrieve the RDFa Profile triples associated with that profile.
173
187
  @host_defaults = case @host_language
174
188
  when :xhtml
175
189
  @graph.bind(XHV_NS)
@@ -188,7 +202,7 @@ module RdfContext
188
202
 
189
203
  @host_defaults.delete(:vocabulary) if @version == :rdfa_1_0
190
204
 
191
- add_debug(@doc, "version = #{@version}, host_language = #{@host_language}")
205
+ add_debug(@doc.root, "version = #{@version.inspect}, host_language = #{@host_language}")
192
206
  # parse
193
207
  parse_whole_document(@doc, @uri)
194
208
 
@@ -206,51 +220,45 @@ module RdfContext
206
220
  base = base_el.attributes['href']
207
221
  # Strip any fragment from base
208
222
  base = base.to_s.split("#").first
209
- @uri = URIRef.new(base, :normalize => false, :normalize => false)
223
+ @uri = URIRef.intern(base, :normalize => false, :normalize => false)
210
224
  add_debug(base_el, "parse_whole_doc: base='#{base}'")
211
225
  end
212
226
 
213
227
  # initialize the evaluation context with the appropriate base
214
- evaluation_context = EvaluationContext.new(base, @host_defaults)
228
+ evaluation_context = EvaluationContext.new(@uri, @host_defaults)
215
229
 
216
230
  traverse(doc.root, evaluation_context)
217
231
  end
218
232
 
219
- # Extract the XMLNS mappings from an element
220
- def extract_mappings(element, uri_mappings, term_mappings)
221
- # Process @profile
222
- # Next the current element is parsed for any updates to the local term mappings and
223
- # local list of URI mappings via @profile.
224
- # If @profile is present, its value is processed as defined in RDFa Profiles.
225
- element.attributes['profile'].to_s.split(/\s/).each do |profile|
233
+ # Parse and process URI mappings, Term mappings and a default vocabulary from @profile
234
+ #
235
+ # Yields each mapping
236
+ def process_profile(element)
237
+ element.attributes['profile'].to_s.split(/\s/).reverse.each do |profile|
226
238
  # Don't try to open ourselves!
227
239
  if @uri == profile
228
- add_debug(element, "extract_mappings: skip recursive profile <#{profile}>")
229
- @@vocabulary_cache[profile]
240
+ add_debug(element, "process_profile: skip recursive profile <#{profile}>")
230
241
  elsif @@vocabulary_cache.has_key?(profile)
231
- add_debug(element, "extract_mappings: skip previously parsed profile <#{profile}>")
242
+ add_debug(element, "process_profile: skip previously parsed profile <#{profile}>")
232
243
  else
233
244
  begin
234
- add_debug(element, "extract_mappings: parse profile <#{profile}>")
235
245
  @@vocabulary_cache[profile] = {
236
246
  :uri_mappings => {},
237
- :term_mappings => {}
247
+ :term_mappings => {},
248
+ :default_vocabulary => nil
238
249
  }
239
250
  um = @@vocabulary_cache[profile][:uri_mappings]
240
251
  tm = @@vocabulary_cache[profile][:term_mappings]
241
- add_debug(element, "extract_mappings: profile open <#{profile}>")
242
- require 'patron' unless defined?(Patron)
243
- sess = Patron::Session.new
244
- sess.timeout = 10
245
- resp = sess.get(profile)
246
- raise RuntimeError, "HTTP returned status #{resp.status} when reading #{profile}" if resp.status >= 400
252
+ add_debug(element, "process_profile: parse profile <#{profile}>")
253
+ prof_body = OpenURI.open_uri(profile)
254
+ raise ParserException, "Empty profile #{profile}" if prof_body.to_s.empty?
247
255
 
248
256
  # Parse profile, and extract mappings from graph
249
- old_debug, old_verbose, = $DEBUG, $verbose
250
- $DEBUG, $verbose = false, false
251
- p_graph = Parser.parse(resp.body, profile)
252
- ttl = p_graph.serialize(:format => :ttl) if @debug || $DEBUG
253
- $DEBUG, $verbose = old_debug, old_verbose
257
+ old_debug, old_verbose, = ::RdfContext::debug?, $verbose
258
+ ::RdfContext::debug, $verbose = false, false
259
+ p_graph = Parser.parse(prof_body, profile)
260
+ ttl = p_graph.serialize(:format => :ttl) if @debug || ::RdfContext::debug?
261
+ ::RdfContext::debug, $verbose = old_debug, old_verbose
254
262
  add_debug(element, ttl) if ttl
255
263
  p_graph.subjects.each do |subject|
256
264
  props = p_graph.properties(subject)
@@ -261,19 +269,24 @@ module RdfContext
261
269
  uri = props[RDFA_NS.uri_.to_s]
262
270
  term = props[RDFA_NS.term_.to_s]
263
271
  prefix = props[RDFA_NS.prefix_.to_s]
264
- add_debug(element, "extract_mappings: uri=#{uri.inspect}, term=#{term.inspect}, prefix=#{prefix.inspect}")
272
+ vocab = props[RDFA_NS.vocabulary.to_s]
273
+ add_debug(element, "process_profile: uri=#{uri.inspect}, term=#{term.inspect}, prefix=#{prefix.inspect}, vocabulary=#{vocab.inspect}")
265
274
 
266
- next if !uri || (!term && !prefix)
267
- raise ParserException, "multi-valued rdf:uri" if uri.length != 1
275
+ raise ParserException, "multi-valued rdf:uri" if uri && uri.length != 1
268
276
  raise ParserException, "multi-valued rdf:term." if term && term.length != 1
269
277
  raise ParserException, "multi-valued rdf:prefix" if prefix && prefix.length != 1
278
+ raise ParserException, "multi-valued rdf:vocabulary" if vocab && vocab.length != 1
270
279
 
271
- uri = uri.first
280
+ uri = uri.first if uri
272
281
  term = term.first if term
273
282
  prefix = prefix.first if prefix
274
- raise ParserException, "rdf:uri must be a Literal" unless uri.is_a?(Literal)
275
- raise ParserException, "rdf:term must be a Literal" unless term.nil? || term.is_a?(Literal)
276
- raise ParserException, "rdf:prefix must be a Literal" unless prefix.nil? || prefix.is_a?(Literal)
283
+ vocab = vocab.first if vocab
284
+ raise ParserException, "rdf:uri #{uri.inspect} must be a Literal" unless uri.nil? || uri.is_a?(Literal)
285
+ raise ParserException, "rdf:term #{term.inspect} must be a Literal" unless term.nil? || term.is_a?(Literal)
286
+ raise ParserException, "rdf:prefix #{prefix.inspect} must be a Literal" unless prefix.nil? || prefix.is_a?(Literal)
287
+ raise ParserException, "rdf:vocabulary #{vocab.inspect} must be a Literal" unless vocab.nil? || vocab.is_a?(Literal)
288
+
289
+ @@vocabulary_cache[profile][:default_vocabulary] = vocab if vocab
277
290
 
278
291
  # For every extracted triple that is the common subject of an rdfa:prefix and an rdfa:uri
279
292
  # predicate, create a mapping from the object literal of the rdfa:prefix predicate to the
@@ -285,38 +298,42 @@ module RdfContext
285
298
  # triple that is the common subject of an rdfa:term and an rdfa:uri predicate, create a
286
299
  # mapping from the object literal of the rdfa:term predicate to the object literal of the
287
300
  # rdfa:uri predicate. Add or update this mapping in the local term mappings.
288
- tm[term.to_s] = URIRef.new(uri.to_s, :normalize => false) if term
301
+ tm[term.to_s.downcase] = URIRef.intern(uri.to_s, :normalize => false) if term
289
302
  end
290
- rescue ParserException
291
- add_debug(element, "extract_mappings: profile subject #{subject.to_s}: #{e.message}")
292
- raise if @strict
293
- rescue RuntimeError => e
294
- add_debug(element, "extract_mappings: profile: #{e.message}")
295
- raise if @strict
303
+ rescue Exception => e
304
+ add_error(element, e.message, RDFA_NS.ProfileReferenceError)
305
+ raise # We need to be sure processing stops
296
306
  end
297
307
  end
298
-
299
- # Merge mappings from this vocabulary
300
- uri_mappings.merge!(@@vocabulary_cache[profile][:uri_mappings])
301
- term_mappings.merge!(@@vocabulary_cache[profile][:term_mappings])
302
- end unless @version == :rdfa_1_0
303
-
308
+ profile_mappings = @@vocabulary_cache[profile]
309
+ yield :uri_mappings, profile_mappings[:uri_mappings] unless profile_mappings[:uri_mappings].empty?
310
+ yield :term_mappings, profile_mappings[:term_mappings] unless profile_mappings[:term_mappings].empty?
311
+ yield :default_vocabulary, profile_mappings[:default_vocabulary] if profile_mappings[:default_vocabulary]
312
+ end
313
+ end
314
+
315
+ # Extract the XMLNS mappings from an element
316
+ def extract_mappings(element, uri_mappings)
304
317
  # look for xmlns
305
318
  # (note, this may be dependent on @host_language)
306
319
  # Regardless of how the mapping is declared, the value to be mapped must be converted to lower case,
307
320
  # and the URI is not processed in any way; in particular if it is a relative path it is
308
321
  # not resolved against the current base.
309
- element.namespaces.each do |attr_name, attr_value|
322
+ element.namespace_definitions.each do |ns|
323
+ add_debug(element, "extract_mappings: namespace #{ns.prefix} => <#{ns.href}>")
310
324
  begin
311
- abbr, prefix = attr_name.split(":")
312
325
  # A Conforming RDFa Processor must ignore any definition of a mapping for the '_' prefix.
313
- next if prefix == "_"
326
+ next if ns.prefix == "_"
314
327
 
315
- pfx_lc = @version == :rdfa_1_0 || prefix.nil? ? prefix : prefix.to_s.downcase
316
- uri_mappings[pfx_lc] = @graph.bind(Namespace.new(attr_value, prefix.to_s)) if abbr.downcase == "xmlns" && prefix
328
+ # Downcase prefix for RDFa 1.1
329
+ pfx_lc = (@version == :rdfa_1_0 || ns.prefix.nil?) ? ns.prefix : ns.prefix.to_s.downcase
330
+ if ns.prefix
331
+ uri_mappings[pfx_lc] = @graph.bind(Namespace.new(ns.href, ns.prefix.to_s))
332
+ add_debug(element, "extract_mappings: xmlns:#{ns.prefix} => <#{ns.href}>")
333
+ end
334
+
317
335
  rescue RdfException => e
318
- add_debug(element, "extract_mappings raised #{e.class}: #{e.message}")
319
- raise if @strict
336
+ add_error(element, "extract_mappings raised #{e.class}: #{e.message}")
320
337
  end
321
338
  end
322
339
 
@@ -335,10 +352,8 @@ module RdfContext
335
352
  next if prefix == "_"
336
353
 
337
354
  uri_mappings[prefix] = @graph.bind(Namespace.new(uri, prefix))
355
+ add_debug(element, "extract_mappings: prefix #{prefix} => <#{uri}>")
338
356
  end unless @version == :rdfa_1_0
339
-
340
- add_debug(element, "uri_mappings: #{uri_mappings.values.map{|ns|ns.to_s}.join(", ")}")
341
- add_debug(element, "term_mappings: #{term_mappings.keys.join(", ")}")
342
357
  end
343
358
 
344
359
  # The recursive helper function
@@ -351,7 +366,7 @@ module RdfContext
351
366
 
352
367
  add_debug(element, "traverse, ec: #{evaluation_context.inspect}")
353
368
 
354
- # local variables [5.5 Step 1]
369
+ # local variables [7.5 Step 1]
355
370
  recurse = true
356
371
  skip = false
357
372
  new_subject = nil
@@ -374,31 +389,54 @@ module RdfContext
374
389
  vocab = attrs['vocab']
375
390
 
376
391
  # Pull out the attributes needed for the skip test.
377
- property = attrs['property'].to_s if attrs['property']
378
- typeof = attrs['typeof'].to_s if attrs['typeof']
392
+ property = attrs['property'].to_s.strip if attrs['property']
393
+ typeof = attrs['typeof'].to_s.strip if attrs['typeof']
379
394
  datatype = attrs['datatype'].to_s if attrs['datatype']
380
395
  content = attrs['content'].to_s if attrs['content']
381
- rel = attrs['rel'].to_s if attrs['rel']
382
- rev = attrs['rev'].to_s if attrs['rev']
396
+ rel = attrs['rel'].to_s.strip if attrs['rel']
397
+ rev = attrs['rev'].to_s.strip if attrs['rev']
383
398
 
384
- # Default vocabulary [7.5 Step 2]
385
- # First the current element is examined for any change to the default vocabulary via @vocab.
399
+ # Local term mappings [7.5 Steps 2]
400
+ # Next the current element is parsed for any updates to the local term mappings and local list of URI mappings via @profile.
401
+ # If @profile is present, its value is processed as defined in RDFa Profiles.
402
+ unless @version == :rdfa_1_0
403
+ begin
404
+ process_profile(element) do |which, value|
405
+ add_debug(element, "[Step 2] traverse, #{which}: #{value.inspect}")
406
+ case which
407
+ when :uri_mappings then uri_mappings.merge!(value)
408
+ when :term_mappings then term_mappings.merge!(value)
409
+ when :default_vocabulary then default_vocabulary = value
410
+ end
411
+ end
412
+ rescue
413
+ # Skip this element and all sub-elements
414
+ # If any referenced RDFa Profile is not available, then the current element and its children must not place any
415
+ # triples in the default graph .
416
+ raise if @strict
417
+ return
418
+ end
419
+ end
420
+
421
+ # Default vocabulary [7.5 Step 3]
422
+ # Next the current element is examined for any change to the default vocabulary via @vocab.
386
423
  # If @vocab is present and contains a value, its value updates the local default vocabulary.
387
424
  # If the value is empty, then the local default vocabulary must be reset to the Host Language defined default.
388
425
  unless vocab.nil?
389
426
  default_vocabulary = if vocab.to_s.empty?
427
+ add_debug(element, "[Step 2] traverse, reset default_vocaulary to #{@host_defaults.fetch(:vocabulary, nil).inspect}")
390
428
  # Set default_vocabulary to host language default
391
429
  @host_defaults.fetch(:vocabulary, nil)
392
430
  else
393
- URIRef.new(vocab)
431
+ URIRef.intern(vocab)
394
432
  end
395
433
  add_debug(element, "[Step 2] traverse, default_vocaulary: #{default_vocabulary.inspect}")
396
434
  end
397
435
 
398
- # Local term mappings [7.5 Steps 3 & 4]
399
- # Next the current element is parsed for any updates to the local term mappings and local list of URI mappings via @profile.
400
- # If @profile is present, its value is processed as defined in RDFa Profiles.
401
- extract_mappings(element, uri_mappings, term_mappings)
436
+ # Local term mappings [7.5 Steps 4]
437
+ # Next, the current element is then examined for URI mapping s and these are added to the local list of URI mappings.
438
+ # Note that a URI mapping will simply overwrite any current mapping in the list that has the same name
439
+ extract_mappings(element, uri_mappings)
402
440
 
403
441
  # Language information [7.5 Step 5]
404
442
  # From HTML5 [3.2.3.3]
@@ -421,12 +459,12 @@ module RdfContext
421
459
  :uri_mappings => uri_mappings,
422
460
  :term_mappings => term_mappings,
423
461
  :vocab => default_vocabulary,
424
- :r_1_0_restrictions => [:uri, :bnode, :term])
462
+ :restrictions => TERMorCURIEorAbsURI[@version])
425
463
  revs = process_uris(element, rev, evaluation_context,
426
464
  :uri_mappings => uri_mappings,
427
465
  :term_mappings => term_mappings,
428
466
  :vocab => default_vocabulary,
429
- :r_1_0_restrictions => [:uri, :bnode, :term])
467
+ :restrictions => TERMorCURIEorAbsURI[@version])
430
468
 
431
469
  add_debug(element, "traverse, about: #{about.nil? ? 'nil' : about}, src: #{src.nil? ? 'nil' : src}, resource: #{resource.nil? ? 'nil' : resource}, href: #{href.nil? ? 'nil' : href}")
432
470
  add_debug(element, "traverse, property: #{property.nil? ? 'nil' : property}, typeof: #{typeof.nil? ? 'nil' : typeof}, datatype: #{datatype.nil? ? 'nil' : datatype}, content: #{content.nil? ? 'nil' : content}")
@@ -438,15 +476,15 @@ module RdfContext
438
476
  new_subject = if about
439
477
  process_uri(element, about, evaluation_context,
440
478
  :uri_mappings => uri_mappings,
441
- :r_1_0_restrictions => [:uri, :safe_curie, :bnode])
479
+ :restrictions => SafeCURIEorCURIEorURI[@version])
442
480
  elsif src
443
- process_uri(element, src, evaluation_context, :r_1_0_restrictions => [:uri])
481
+ process_uri(element, src, evaluation_context, :restrictions => [:uri])
444
482
  elsif resource
445
483
  process_uri(element, resource, evaluation_context,
446
484
  :uri_mappings => uri_mappings,
447
- :r_1_0_restrictions => [:uri, :safe_curie, :bnode])
485
+ :restrictions => SafeCURIEorCURIEorURI[@version])
448
486
  elsif href
449
- process_uri(element, href, evaluation_context, :r_1_0_restrictions => [:uri])
487
+ process_uri(element, href, evaluation_context, :restrictions => [:uri])
450
488
  end
451
489
 
452
490
  # If no URI is provided by a resource attribute, then the first match from the following rules
@@ -459,7 +497,7 @@ module RdfContext
459
497
  # From XHTML+RDFa 1.1:
460
498
  # if no URI is provided, then first check to see if the element is the head or body element.
461
499
  # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
462
- URIRef.new(evaluation_context.base, :normalize => false)
500
+ evaluation_context.base
463
501
  elsif element.attributes['typeof']
464
502
  BNode.new
465
503
  else
@@ -472,16 +510,19 @@ module RdfContext
472
510
  # [7.5 Step 7]
473
511
  # If the current element does contain a @rel or @rev attribute, then the next step is to
474
512
  # establish both a value for new subject and a value for current object resource:
475
- new_subject = process_uri(element, about || src, evaluation_context,
513
+ new_subject = process_uri(element, about, evaluation_context,
476
514
  :uri_mappings => uri_mappings,
477
- :r_1_0_restrictions => [:uri, :safe_curie, :bnode])
478
-
515
+ :restrictions => SafeCURIEorCURIEorURI[@version]) ||
516
+ process_uri(element, src, evaluation_context,
517
+ :uri_mappings => uri_mappings,
518
+ :restrictions => [:uri])
519
+
479
520
  # If no URI is provided then the first match from the following rules will apply
480
521
  new_subject ||= if @host_language == :xhtml && element.name =~ /^(head|body)$/
481
522
  # From XHTML+RDFa 1.1:
482
523
  # if no URI is provided, then first check to see if the element is the head or body element.
483
524
  # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
484
- URIRef.new(evaluation_context.base, :normalize => false)
525
+ evaluation_context.base
485
526
  elsif element.attributes['typeof']
486
527
  BNode.new
487
528
  else
@@ -494,10 +535,10 @@ module RdfContext
494
535
  current_object_resource = if resource
495
536
  process_uri(element, resource, evaluation_context,
496
537
  :uri_mappings => uri_mappings,
497
- :r_1_0_restrictions => [:uri, :safe_curie, :bnode])
538
+ :restrictions => SafeCURIEorCURIEorURI[@version])
498
539
  elsif href
499
540
  process_uri(element, href, evaluation_context,
500
- :r_1_0_restrictions => [:uri])
541
+ :restrictions => [:uri])
501
542
  end
502
543
 
503
544
  add_debug(element, "[Step 7] new_subject: #{new_subject}, current_object_resource = #{current_object_resource.nil? ? 'nil' : current_object_resource}")
@@ -505,12 +546,12 @@ module RdfContext
505
546
 
506
547
  # Process @typeof if there is a subject [Step 8]
507
548
  if new_subject and typeof
508
- # Typeof is TERMorCURIEorURIs
549
+ # Typeof is TERMorCURIEorAbsURIs
509
550
  types = process_uris(element, typeof, evaluation_context,
510
551
  :uri_mappings => uri_mappings,
511
552
  :term_mappings => term_mappings,
512
553
  :vocab => default_vocabulary,
513
- :r_1_0_restrictions => [:curie, :bnode])
554
+ :restrictions => TERMorCURIEorAbsURI[@version])
514
555
  add_debug(element, "typeof: #{typeof}")
515
556
  types.each do |one_type|
516
557
  add_triple(element, new_subject, RDF_TYPE, one_type)
@@ -546,7 +587,7 @@ module RdfContext
546
587
  :uri_mappings => uri_mappings,
547
588
  :term_mappings => term_mappings,
548
589
  :vocab => default_vocabulary,
549
- :r_1_0_restrictions => [:curie, :bnode])
590
+ :restrictions => TERMorCURIEorAbsURIprop[@version])
550
591
 
551
592
  properties.reject! do |p|
552
593
  if p.is_a?(URIRef)
@@ -559,28 +600,40 @@ module RdfContext
559
600
  end
560
601
 
561
602
  # get the literal datatype
562
- type = datatype
563
603
  children_node_types = element.children.collect{|c| c.class}.uniq
564
604
 
565
605
  # the following 3 IF clauses should be mutually exclusive. Written as is to prevent extensive indentation.
566
- type_resource = process_uri(element, type, evaluation_context,
567
- :uri_mappings => uri_mappings,
568
- :term_mappings => term_mappings,
569
- :vocab => default_vocabulary,
570
- :r_1_0_restrictions => [:curie, :bnode]) if type
571
- current_object_literal = if type and !type.empty? and (type_resource.to_s != XML_LITERAL.to_s)
606
+ datatype = process_uri(element, datatype, evaluation_context,
607
+ :uri_mappings => uri_mappings,
608
+ :term_mappings => term_mappings,
609
+ :vocab => default_vocabulary,
610
+ :restrictions => TERMorCURIEorAbsURI[@version]) unless datatype.to_s.empty?
611
+ current_object_literal = if !datatype.to_s.empty? && datatype.to_s != XML_LITERAL.to_s
572
612
  # typed literal
573
613
  add_debug(element, "[Step 11] typed literal")
574
- Literal.typed(content || element.inner_text, type_resource, :language => language)
575
- elsif content or (children_node_types == [Nokogiri::XML::Text]) or (element.children.length == 0) or (type == '')
576
- # plain literal
577
- add_debug(element, "[Step 11] plain literal")
578
- Literal.untyped(content || element.inner_text, language)
579
- elsif children_node_types != [Nokogiri::XML::Text] and (type == nil or type_resource.to_s == XML_LITERAL.to_s)
580
- # XML Literal
581
- add_debug(element, "[Step 11] XML Literal: #{element.inner_html}")
582
- recurse = false
583
- Literal.typed(element.children, XML_LITERAL, :language => language, :namespaces => uri_mappings)
614
+ Literal.typed(content || element.inner_text, datatype, :language => language)
615
+ elsif @version == :rdfa_1_1
616
+ if datatype.to_s == XML_LITERAL.to_s
617
+ # XML Literal
618
+ add_debug(element, "[Step 11(1.1)] XML Literal: #{element.inner_html}")
619
+ recurse = false
620
+ Literal.typed(element.children, XML_LITERAL, :language => language, :namespaces => uri_mappings)
621
+ else
622
+ # plain literal
623
+ add_debug(element, "[Step 11(1.1)] plain literal")
624
+ Literal.untyped(content || element.inner_text, language)
625
+ end
626
+ else
627
+ if content || (children_node_types == [Nokogiri::XML::Text]) || (element.children.length == 0) || datatype == ""
628
+ # plain literal
629
+ add_debug(element, "[Step 11 (1.0)] plain literal")
630
+ Literal.untyped(content || element.inner_text, language)
631
+ elsif children_node_types != [Nokogiri::XML::Text] and (datatype == nil or datatype.to_s == XML_LITERAL.to_s)
632
+ # XML Literal
633
+ add_debug(element, "[Step 11 (1.0)] XML Literal: #{element.inner_html}")
634
+ recurse = false
635
+ Literal.typed(element.children, XML_LITERAL, :language => language, :namespaces => uri_mappings)
636
+ end
584
637
  end
585
638
 
586
639
  # add each property
@@ -640,7 +693,7 @@ module RdfContext
640
693
  end
641
694
  end
642
695
 
643
- # space-separated TERMorCURIEorURI
696
+ # space-separated TERMorCURIEorAbsURI or SafeCURIEorCURIEorURI
644
697
  def process_uris(element, value, evaluation_context, options)
645
698
  return [] if value.to_s.empty?
646
699
  add_debug(element, "process_uris: #{value}")
@@ -649,8 +702,8 @@ module RdfContext
649
702
 
650
703
  def process_uri(element, value, evaluation_context, options = {})
651
704
  return if value.nil?
652
- restrictions = @version == :rdfa_1_0 ? options[:r_1_0_restrictions] : [:uri, :bnode, :curie, :safe_curie, :term]
653
- add_debug(element, "process_uri: restrictions = #{restrictions.inspect}")
705
+ restrictions = options[:restrictions]
706
+ add_debug(element, "process_uri: #{value}, restrictions = #{restrictions.inspect}")
654
707
  options = {:uri_mappings => {}}.merge(options)
655
708
  if !options[:term_mappings] && options[:uri_mappings] && value.to_s.match(/^\[(.*)\]$/) && restrictions.include?(:safe_curie)
656
709
  # SafeCURIEorCURIEorURI
@@ -659,25 +712,38 @@ module RdfContext
659
712
  # value must be ignored.
660
713
  uri = curie_to_resource_or_bnode(element, $1, options[:uri_mappings], evaluation_context.parent_subject, restrictions)
661
714
  add_debug(element, "process_uri: #{value} => safeCURIE => <#{uri}>")
715
+
662
716
  uri
663
717
  elsif options[:term_mappings] && NC_REGEXP.match(value.to_s) && restrictions.include?(:term)
664
- # TERMorCURIEorURI
718
+ # TERMorCURIEorAbsURI
665
719
  # If the value is an NCName, then it is evaluated as a term according to General Use of Terms in
666
720
  # Attributes. Note that this step may mean that the value is to be ignored.
667
- uri = process_term(value.to_s, options)
721
+ uri = process_term(element, value.to_s, options)
668
722
  add_debug(element, "process_uri: #{value} => term => <#{uri}>")
669
723
  uri
670
724
  else
671
- # SafeCURIEorCURIEorURI or TERMorCURIEorURI
725
+ # SafeCURIEorCURIEorURI or TERMorCURIEorAbsURI
672
726
  # Otherwise, the value is evaluated as a CURIE.
673
727
  # If it is a valid CURIE, the resulting URI is used; otherwise, the value will be processed as a URI.
674
728
  uri = curie_to_resource_or_bnode(element, value, options[:uri_mappings], evaluation_context.parent_subject, restrictions)
675
729
  if uri
676
730
  add_debug(element, "process_uri: #{value} => CURIE => <#{uri}>")
677
- elsif @version == :rdfa_1_0 && value.to_s.match(/^xml/)
731
+ elsif @version == :rdfa_1_0 && value.to_s.match(/^xml/i)
678
732
  # Special case to not allow anything starting with XML to be treated as a URI
679
- else
680
- uri = URIRef.new(value, evaluation_context.base, :normalize => false)
733
+ elsif restrictions.include?(:absuri) || restrictions.include?(:uri)
734
+ begin
735
+ # AbsURI does not use xml:base
736
+ uri = URIRef.intern(value, restrictions.include?(:absuri) ? nil : evaluation_context.base, :normalize => false)
737
+ rescue Addressable::URI::InvalidURIError => e
738
+ add_warning(element, "Malformed prefix #{value}", RDFA_NS.UndefinedPrefixError)
739
+ rescue ParserException => e
740
+ add_debug(element, e.message)
741
+ if value.to_s =~ /^\(^\w\):/
742
+ add_warning(element, "Undefined prefix #{$1}", RDFA_NS.UndefinedPrefixError)
743
+ else
744
+ add_warning(element, "Relative URI #{value}")
745
+ end
746
+ end
681
747
  add_debug(element, "process_uri: #{value} => URI => <#{uri}>")
682
748
  end
683
749
  uri
@@ -690,17 +756,17 @@ module RdfContext
690
756
  # @param [Hash] options:: Parser options, one of
691
757
  # <em>options[:term_mappings]</em>:: Term mappings
692
758
  # <em>options[:vocab]</em>:: Default vocabulary
693
- def process_term(value, options)
759
+ def process_term(element, value, options)
694
760
  case
695
761
  when options[:term_mappings].is_a?(Hash) && options[:term_mappings].has_key?(value.to_s.downcase)
696
762
  # If the term is in the local term mappings, use the associated URI.
697
- # XXX Spec Confusion: are terms always downcased? Or only for XHTML Vocab?
698
763
  options[:term_mappings][value.to_s.downcase]
699
764
  when options[:vocab]
700
765
  # Otherwise, if there is a local default vocabulary the URI is obtained by concatenating that value and the term.
701
- URIRef.new(options[:vocab].to_s + value)
766
+ URIRef.intern(options[:vocab].to_s + value)
702
767
  else
703
768
  # Finally, if there is no local default vocabulary, the term has no associated URI and must be ignored.
769
+ add_warning(element, "Term #{value} is not defined", RDFA_NS.UndefinedTermError)
704
770
  nil
705
771
  end
706
772
  end
@@ -713,6 +779,7 @@ module RdfContext
713
779
  # consider the bnode situation
714
780
  if prefix == "_" && restrictions.include?(:bnode)
715
781
  # we force a non-nil name, otherwise it generates a new name
782
+ # As a special case, _: is also a valid reference for one specific bnode.
716
783
  BNode.new(reference || "", @named_bnodes)
717
784
  elsif curie.to_s.match(/^:/)
718
785
  add_debug(element, "curie_to_resource_or_bnode: default prefix: defined? #{!!uri_mappings[""]}, defaults: #{@host_defaults[:prefix]}")
@@ -721,6 +788,9 @@ module RdfContext
721
788
  uri_mappings[""].send("#{reference}_")
722
789
  elsif @host_defaults[:prefix]
723
790
  @host_defaults[:prefix].send("#{reference}_")
791
+ else
792
+ #add_warning(element, "Default namespace prefix is not defined", RDFA_NS.UndefinedPrefixError)
793
+ nil
724
794
  end
725
795
  elsif !curie.to_s.match(/:/)
726
796
  # No prefix, undefined (in this context, it is evaluated as a term elsewhere)
@@ -732,7 +802,7 @@ module RdfContext
732
802
  if ns
733
803
  ns + reference
734
804
  else
735
- add_debug(element, "curie_to_resource_or_bnode No namespace mapping for #{prefix}")
805
+ #add_debug(element, "curie_to_resource_or_bnode No namespace mapping for #{prefix}")
736
806
  nil
737
807
  end
738
808
  end