rdf_context 0.5.3 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,15 @@
1
+ === 0.5.4
2
+ * RDFa 1.1 parsing supported (based on RDFa Core 1.1 W3C Working Draft 22 April 2010)
3
+ * Fix URIRef#short_name (and consequently #base and #namespace) to not extract a non-hierarchical path as a short_name
4
+ * Namespace no longer uses URIRef, but just acts on strings.
5
+ * Namespace#new does not take an optional _fragment_ argument any longer.
6
+ * Added Namespace#to_s to output "prefix: uri" format
7
+ * Graph#qname first trys generating using bound namespaces, then adds well-known namespaces.
8
+ * URIRef#to_qname and #to_namespace No longer generates an exception. Each take either a Hash or an Array of namespaces and tries them from longest to shortest.
9
+ * Improved Turtle and XML serializers in use of namespaces.
10
+ * Generate pending messages if RDFa tests skipped due to lack of Redland installation.
11
+ * Change dcterms: prefix to dc: (fully compatible with previous /elements/ definitions)
12
+
1
13
  === 0.5.3
2
14
  * Fix bug in XML serializer when type is a BNode.
3
15
  * Undo change in 0.5.2 to remove old binding based on equivalent URI to equivalent prefix.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.3
1
+ 0.5.5
data/bin/rdf_context CHANGED
@@ -5,7 +5,7 @@ require 'getoptlong'
5
5
  class Parse
6
6
  include RdfContext
7
7
  def parse(file, base_uri, store = nil)
8
- puts "Parse: #{file}" if $quiet
8
+ puts "Parse: #{file.is_a?(StringIO) ? base_uri : file}" if $quiet
9
9
  graph_opts = {:identifier => base_uri}
10
10
  graph_opts[:store] = store if store
11
11
  parser = Parser.new(:graph => Graph.new(graph_opts))
@@ -25,6 +25,38 @@ class Parse
25
25
  end
26
26
  end
27
27
 
28
+ TEST_DIR = File.join(File.dirname(__FILE__), '..', 'spec', 'rdfa-test-suite', 'tests')
29
+ BASE_MANIFEST_URL = "http://rdfa.digitalbazaar.com/test-suite/"
30
+ BASE_TEST_CASE_URL = "#{BASE_MANIFEST_URL}test-cases/"
31
+
32
+ def rdfa_tc(number, parse, store)
33
+ f = File.expand_path("#{TEST_DIR}/#{number}.txt")
34
+ found_head = false
35
+ namespaces = ""
36
+ body = File.readlines(f).map do |line|
37
+ found_head ||= line.match(/<head/)
38
+ if found_head
39
+ line.chop
40
+ else
41
+ namespaces << line
42
+ nil
43
+ end
44
+ end.compact.join("\n")
45
+
46
+ namespaces.chop! # Remove trailing newline
47
+
48
+ tcpath = BASE_TEST_CASE_URL + "xhtml1"
49
+
50
+ head = "" +
51
+ %(<?xml version="1.0" encoding="UTF-8"?>\n) +
52
+ %(<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">\n) +
53
+ %(<html xmlns="http://www.w3.org/1999/xhtml" version="XHTML+RDFa 1.1"\n)
54
+ tc = head + "#{namespaces}>\n#{body.gsub(/\$TCPATH/, tcpath)}\n</html>"
55
+
56
+ puts "Input file: #{tc}" if $DEBUG || $verbose
57
+ parse.parse(StringIO.new(tc), "#{tcpath}/#{number}.xhtml")
58
+ end
59
+
28
60
  mode = ARGV.shift
29
61
  raise "Mode must be one of 'parse'" unless mode == "parse"
30
62
 
@@ -38,7 +70,8 @@ opts = GetoptLong.new(
38
70
  ["--debug", GetoptLong::NO_ARGUMENT],
39
71
  ["--format", GetoptLong::REQUIRED_ARGUMENT],
40
72
  ["--store", GetoptLong::REQUIRED_ARGUMENT],
41
- ["--uri", GetoptLong::REQUIRED_ARGUMENT]
73
+ ["--uri", GetoptLong::REQUIRED_ARGUMENT],
74
+ ["--rdfa", GetoptLong::NO_ARGUMENT]
42
75
  )
43
76
  opts.each do |opt, arg|
44
77
  case opt
@@ -47,6 +80,7 @@ opts.each do |opt, arg|
47
80
  when '--debug' then $DEBUG = true
48
81
  when '--format' then $format = arg
49
82
  when '--uri' then base_uri = arg
83
+ when '--rdfa' then $rdfa = true
50
84
  when '--store'
51
85
  case arg
52
86
  when /list/
@@ -61,9 +95,20 @@ opts.each do |opt, arg|
61
95
  end
62
96
 
63
97
  x = Parse.new
64
- if ARGV.empty?
98
+ if $rdfa && ARGV.empty?
99
+ # Run all RDFa test cases
100
+ Dir.foreach(TEST_DIR) do |f|
101
+ next unless f.match(/^(.*)\.txt$/)
102
+ rdfa_tc($1, x, store)
103
+ end
104
+ elsif ARGV.empty?
65
105
  s = $stdin.read
66
106
  x.parse(StringIO.new(s), base_uri, store)
107
+ elsif $rdfa
108
+ # Run specified RDFa test cases
109
+ ARGV.each do |tc|
110
+ rdfa_tc(tc, x, store)
111
+ end
67
112
  else
68
113
  ARGV.each do |test_file|
69
114
  x.parse(test_file, base_uri, store)
data/lib/rdf_context.rb CHANGED
@@ -9,6 +9,7 @@ rescue LoadError
9
9
  require 'rubygems' unless ENV['NO_RUBYGEMS']
10
10
  gem 'nokogiri'
11
11
  gem 'addressable'
12
+ gem 'treetop'
12
13
  require 'nokogiri'
13
14
  require 'addressable/uri'
14
15
  require 'builder'
@@ -60,15 +61,17 @@ module RdfContext
60
61
  RDF_TYPE = URIRef.new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
61
62
  XML_LITERAL = Literal::Encoding.xmlliteral
62
63
 
63
- DC_NS = Namespace.new("http://purl.org/dc/elements/1.1/", "dc")
64
+ DC_NS = Namespace.new("http://purl.org/dc/terms/", "dc")
64
65
  OWL_NS = Namespace.new("http://www.w3.org/2002/07/owl#", "owl")
65
66
  LOG_NS = Namespace.new("http://www.w3.org/2000/10/swap/log#", "log")
66
67
  RDF_NS = Namespace.new("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf")
68
+ RDFA_NS = Namespace.new("http://www.w3.org/ns/rdfa#", "rdfa")
67
69
  RDFS_NS = Namespace.new("http://www.w3.org/2000/01/rdf-schema#", "rdfs")
68
70
  XHV_NS = Namespace.new("http://www.w3.org/1999/xhtml/vocab#", "xhv")
69
71
  XML_NS = Namespace.new("http://www.w3.org/XML/1998/namespace", "xml")
70
72
  XSD_NS = Namespace.new("http://www.w3.org/2001/XMLSchema#", "xsd")
71
73
  XSI_NS = Namespace.new("http://www.w3.org/2001/XMLSchema-instance", "xsi")
74
+ WELLKNOWN_NAMESPACES = [DC_NS, OWL_NS, LOG_NS, RDF_NS, RDFA_NS, RDFS_NS, XHV_NS, XML_NS, XSD_NS, XSI_NS]
72
75
 
73
76
  XH_MAPPING = {"" => Namespace.new("http://www.w3.org/1999/xhtml/vocab\#", nil)}
74
77
  end
@@ -2,6 +2,7 @@ module RdfContext
2
2
  class RdfException < RuntimeError; end
3
3
 
4
4
  class ParserException < RdfException; end
5
+ class SparqlException < RdfException; end
5
6
  class GraphException < RdfException; end
6
7
  class StoreException < RdfException; end
7
8
  class BNodeException < RdfException; end
@@ -163,8 +163,13 @@ module RdfContext
163
163
  def uri_binding; @store.uri_binding; end
164
164
 
165
165
  # QName for a URI
166
+ # Try bound namespaces, and if not found, try well-known namespaces
166
167
  def qname(uri)
167
- uri.to_qname(self.uri_binding)
168
+ uri.to_qname(self.uri_binding) || begin
169
+ qn = uri.to_qname(WELLKNOWN_NAMESPACES)
170
+ self.bind(uri.namespace) if qn
171
+ qn
172
+ end
168
173
  end
169
174
 
170
175
  # Namespace for prefix
@@ -12,21 +12,14 @@ module RdfContext
12
12
  #
13
13
  # @param [String] uri:: the URI of the namespace
14
14
  # @param [String] prefix:: the prefix of the namespace
15
- # @param [Boolean] fragment:: are the identifiers on this resource fragment identifiers? (e.g. '#') Defaults to false.
16
15
  # @return [Namespace]:: The newly created namespace.
17
16
  # @raise [Error]:: Checks validity of the desired prefix and raises if it is incorrect.
18
17
  #
19
18
  # @author Tom Morris, Pius Uzamere
20
- def initialize(uri, prefix, fragment = nil)
19
+ def initialize(uri, prefix)
21
20
  prefix = prefix.to_s
22
21
 
23
- @fragment = fragment
24
- if uri.to_s.match(/^(.*)\#$/)
25
- # Remove frag hash from URI so URIRef doesn't screw it up
26
- uri = $1
27
- @fragment ||= true
28
- end
29
- @uri = uri.is_a?(URIRef) ? uri : URIRef.new(uri, :normalize => false, :namespace => self)
22
+ @uri = uri.to_s
30
23
 
31
24
  raise ParserException, "Invalid prefix '#{prefix}'" unless prefix_valid?(prefix)
32
25
  @prefix = prefix
@@ -41,7 +34,7 @@ module RdfContext
41
34
  #
42
35
  # To avoid naming problems, a suffix may have an appended '_', which will be removed when the URI is generated.
43
36
  #
44
- # @return [URIRef]:: The newly created URIRegerence.
37
+ # @return [String]:: The newly created URI.
45
38
  # @raise [Error]:: Checks validity of the desired prefix and raises if it is incorrect.
46
39
  # @author Tom Morris, Pius Uzamere
47
40
  def method_missing(methodname, *args)
@@ -52,11 +45,10 @@ module RdfContext
52
45
  # Rules are somewhat different than for normal URI unions, as the raw URI is used as the source,
53
46
  # not a normalized URI, and the result is not normalized
54
47
  def +(suffix)
55
- prefix = @uri.to_s
56
- prefix += '#' if @fragment && !prefix.index("#")
48
+ prefix = @uri
57
49
  suffix = suffix.to_s.sub(/^\#/, "") if prefix.index("#")
58
50
  suffix = suffix.to_s.sub(/_$/, '')
59
- URIRef.new(prefix + suffix, :normalize => false, :namespace => self)
51
+ URIRef.new(prefix + suffix.to_s, :normalize => false, :namespace => self)
60
52
  end
61
53
 
62
54
  # Make sure to attach fragment
@@ -71,7 +63,7 @@ module RdfContext
71
63
 
72
64
  # Compare namespaces
73
65
  def eql?(other)
74
- @prefix == other.prefix && self.uri == other.uri && @fragment == other.fragment
66
+ self.uri == other.uri
75
67
  end
76
68
  alias_method :==, :eql?
77
69
 
@@ -85,6 +77,10 @@ module RdfContext
85
77
  {xmlns_attr => @uri.to_s}
86
78
  end
87
79
 
80
+ def to_s
81
+ "#{prefix}: #{@uri}"
82
+ end
83
+
88
84
  def inspect
89
85
  "Namespace[abbr='#{prefix}',uri='#{@uri}']"
90
86
  end
@@ -92,7 +88,7 @@ module RdfContext
92
88
  private
93
89
  # The Namespace prefix must be an NCName
94
90
  def prefix_valid?(prefix)
95
- NC_REGEXP.match(prefix) || prefix.to_s.empty?
91
+ NC_REGEXP.match(prefix.to_s) || prefix.to_s.empty?
96
92
  end
97
93
  end
98
94
  end
@@ -7,7 +7,7 @@ module RdfContext
7
7
  attr_accessor :doc, :graph
8
8
 
9
9
  ##
10
- # Creates a new parser for N3 (or Turtle).
10
+ # Creates a new parser
11
11
  #
12
12
  # @param [Hash] options:: Options from
13
13
  # <em>options[:graph]</em>:: Graph to parse into, otherwise a new RdfContext::Graph instance is created
@@ -4,25 +4,58 @@ module RdfContext
4
4
  ##
5
5
  # An RDFa parser in Ruby
6
6
  #
7
- # Based on processing rules described here: http://www.w3.org/TR/rdfa-syntax/#s_model
7
+ # Based on processing rules described here:
8
+ # file:///Users/gregg/Projects/rdf_context/RDFa%20Core%201.1.html#sequence
8
9
  #
9
10
  # Ben Adida
10
11
  # 2008-05-07
11
12
  # Gregg Kellogg
12
13
  # 2009-08-04
13
14
  class RdfaParser < Parser
14
- attr_reader :namespace
15
-
15
+ # Host language, One of:
16
+ # :xhtml_rdfa_1_0
17
+ # :xhtml_rdfa_1_1
18
+ attr_reader :host_language
19
+
16
20
  # The Recursive Baggage
17
21
  class EvaluationContext # :nodoc:
22
+ # The base. This will usually be the URL of the document being processed,
23
+ # but it could be some other URL, set by some other mechanism,
24
+ # such as the (X)HTML base element. The important thing is that it establishes
25
+ # a URL against which relative paths can be resolved.
18
26
  attr :base, true
27
+ # The parent subject.
28
+ # The initial value will be the same as the initial value of base,
29
+ # but it will usually change during the course of processing.
19
30
  attr :parent_subject, true
31
+ # The parent object.
32
+ # In some situations the object of a statement becomes the subject of any nested statements,
33
+ # and this property is used to convey this value.
34
+ # Note that this value may be a bnode, since in some situations a number of nested statements
35
+ # are grouped together on one bnode.
36
+ # This means that the bnode must be set in the containing statement and passed down,
37
+ # and this property is used to convey this value.
20
38
  attr :parent_object, true
39
+ # A list of current, in-scope URI mappings.
21
40
  attr :uri_mappings, true
41
+ # A list of incomplete triples. A triple can be incomplete when no object resource
42
+ # is provided alongside a predicate that requires a resource (i.e., @rel or @rev).
43
+ # The triples can be completed when a resource becomes available,
44
+ # which will be when the next subject is specified (part of the process called chaining).
22
45
  attr :incomplete_triples, true
46
+ # The language. Note that there is no default language.
23
47
  attr :language, true
48
+ # The term mappings, a list of terms and their associated URIs.
49
+ # This specification does not define an initial list.
50
+ # Host Languages may define an initial list.
51
+ # If a Host Language provides an initial list, it should do so via an RDFa Profile document.
52
+ attr :term_mappings, true
53
+ # The default vocabulary, a value to use as the prefix URI when a term is used.
54
+ # This specification does not define an initial setting for the default vocabulary.
55
+ # Host Languages may define an initial setting.
56
+ attr :default_vocabulary, true
24
57
 
25
- def initialize(base)
58
+ def initialize(base, host_defaults)
26
59
  # Initialize the evaluation context, [5.1]
27
60
  @base = base
28
61
  @parent_subject = @base
@@ -30,6 +63,8 @@ module RdfContext
30
63
  @uri_mappings = {}
31
64
  @incomplete_triples = []
32
65
  @language = nil
66
+ @term_mappings = host_defaults.fetch(:term_mappings, {})
67
+ @default_voabulary = host_defaults.fetch(:voabulary, nil)
33
68
  end
34
69
 
35
70
  # Copy this Evaluation Context
@@ -40,13 +75,27 @@ module RdfContext
40
75
  end
41
76
 
42
77
  def inspect
43
- v = %w(base parent_subject parent_object language).map {|a| "#{a}='#{self.send(a).nil? ? 'nil' : self.send(a)}'"}
78
+ v = %w(base parent_subject parent_object language default_vocabulary).map {|a| "#{a}='#{self.send(a).nil? ? '<nil>' : self.send(a)}'"}
44
79
  v << "uri_mappings[#{uri_mappings.keys.length}]"
45
80
  v << "incomplete_triples[#{incomplete_triples.length}]"
81
+ v << "term_mappings[#{term_mappings.keys.length}]"
46
82
  v.join(",")
47
83
  end
48
84
  end
49
85
 
86
+ ##
87
+ # Creates a new parser for RDFa.
88
+ #
89
+ # @param [Hash] options:: Options from
90
+ # <em>options[:graph]</em>:: Graph to parse into, otherwise a new RdfContext::Graph instance is created
91
+ # <em>options[:debug]</em>:: Array to place debug messages
92
+ # <em>options[:type]</em>:: One of _rdfxml_, _html_, or _n3_
93
+ # <em>options[:strict]</em>:: Raise Error if true, continue with lax parsing, otherwise
94
+ def initialize(options = {})
95
+ super
96
+ @@vocabulary_cache ||= {}
97
+ end
98
+
50
99
  # Parse XHTML+RDFa document from a string or input stream to closure or graph.
51
100
  #
52
101
  # If the parser is called with a block, triples are passed to the block rather
@@ -74,10 +123,29 @@ module RdfContext
74
123
  raise ParserException, "Empty document" if @doc.nil? && @strict
75
124
  @callback = block
76
125
 
77
- # If the doc has a default, use that as "html"
78
- ns = @doc.namespaces["xmlns"]
79
- ns ||= "http://www.w3.org/1999/xhtml" # FIXME: intuite from DOCTYPE, or however
80
- @namespace = Namespace.new(ns, "html") if ns
126
+ # Determine host language
127
+ # XXX - right now only XHTML defined
128
+ @host_language = case @doc.root.attributes["version"].to_s
129
+ when /XHTML+RDFa/ then :xhtml
130
+ end
131
+
132
+ # If none found, assume xhtml
133
+ @host_language ||= :xhtml
134
+
135
+ @host_defaults = case @host_language
136
+ when :xhtml
137
+ @graph.bind(XHV_NS)
138
+ {
139
+ :vocabulary => XHV_NS.uri,
140
+ :prefix => XHV_NS,
141
+ :term_mappings => %w(
142
+ alternate appendix bookmark cite chapter contents copyright first glossary help icon index
143
+ last license meta next p3pv1 prev role section stylesheet subsection start top up
144
+ ).inject({}) { |hash, term| hash[term] = XHV_NS.send("#{term}_"); hash },
145
+ }
146
+ else
147
+ {}
148
+ end
81
149
 
82
150
  # parse
83
151
  parse_whole_document(@doc, @uri)
@@ -90,6 +158,7 @@ module RdfContext
90
158
  # Parsing an RDFa document (this is *not* the recursive method)
91
159
  def parse_whole_document(doc, base)
92
160
  # find if the document has a base element
161
+ # XXX - HTML specific
93
162
  base_el = doc.css('html>head>base').first
94
163
  if (base_el)
95
164
  base = base_el.attributes['href']
@@ -100,28 +169,127 @@ module RdfContext
100
169
  end
101
170
 
102
171
  # initialize the evaluation context with the appropriate base
103
- evaluation_context= EvaluationContext.new(base)
172
+ evaluation_context = EvaluationContext.new(base, @host_defaults)
104
173
 
105
174
  traverse(doc.root, evaluation_context)
106
175
  end
107
176
 
108
177
  # Extract the XMLNS mappings from an element
109
- def extract_mappings(element)
110
- mappings = {}
111
-
178
+ def extract_mappings(element, uri_mappings, term_mappings)
179
+ # Process @profile
180
+ # Next the current element is parsed for any updates to the local term mappings and
181
+ # local list of URI mappings via @profile.
182
+ # If @profile is present, its value is processed as defined in RDFa Profiles.
183
+ element.attributes['profile'].to_s.split(/\s/).each do |profile|
184
+ # Don't try to open ourselves!
185
+ if @uri == profile
186
+ add_debug(element, "extract_mappings: skip recursive profile <#{profile}>")
187
+ @@vocabulary_cache[profile]
188
+ elsif @@vocabulary_cache.has_key?(profile)
189
+ add_debug(element, "extract_mappings: skip previously parsed profile <#{profile}>")
190
+ else
191
+ begin
192
+ add_debug(element, "extract_mappings: parse profile <#{profile}>")
193
+ @@vocabulary_cache[profile] = {
194
+ :uri_mappings => {},
195
+ :term_mappings => {}
196
+ }
197
+ um = @@vocabulary_cache[profile][:uri_mappings]
198
+ tm = @@vocabulary_cache[profile][:term_mappings]
199
+ add_debug(element, "extract_mappings: profile open <#{profile}>")
200
+ require 'patron' unless defined?(Patron)
201
+ sess = Patron::Session.new
202
+ sess.timeout = 10
203
+ resp = sess.get(profile)
204
+ raise RuntimeError, "HTTP returned status #{resp.status} when reading #{profile}" if resp.status >= 400
205
+
206
+ # Parse profile, and extract mappings from graph
207
+ old_debug, old_verbose, = $DEBUG, $verbose
208
+ $DEBUG, $verbose = false, false
209
+ p_graph = Parser.parse(resp.body, profile)
210
+ ttl = p_graph.serialize(:format => :ttl) if @debug || $DEBUG
211
+ $DEBUG, $verbose = old_debug, old_verbose
212
+ add_debug(element, ttl) if ttl
213
+ p_graph.subjects.each do |subject|
214
+ props = p_graph.properties(subject)
215
+ #puts props.inspect
216
+
217
+ # If one of the objects is not a Literal or if there are additional rdfa:uri or rdfa:term
218
+ # predicates sharing the same subject, no mapping is created.
219
+ uri = props[RDFA_NS.uri_.to_s]
220
+ term = props[RDFA_NS.term_.to_s]
221
+ prefix = props[RDFA_NS.prefix_.to_s]
222
+ add_debug(element, "extract_mappings: uri=#{uri.inspect}, term=#{term.inspect}, prefix=#{prefix.inspect}")
223
+
224
+ next if !uri || (!term && !prefix)
225
+ raise ParserException, "multi-valued rdf:uri" if uri.length != 1
226
+ raise ParserException, "multi-valued rdf:term." if term && term.length != 1
227
+ raise ParserException, "multi-valued rdf:prefix" if prefix && prefix.length != 1
228
+
229
+ uri = uri.first
230
+ term = term.first if term
231
+ prefix = prefix.first if prefix
232
+ raise ParserException, "rdf:uri must be a Literal" unless uri.is_a?(Literal)
233
+ raise ParserException, "rdf:term must be a Literal" unless term.nil? || term.is_a?(Literal)
234
+ raise ParserException, "rdf:prefix must be a Literal" unless prefix.nil? || prefix.is_a?(Literal)
235
+
236
+ # For every extracted triple that is the common subject of an rdfa:prefix and an rdfa:uri
237
+ # predicate, create a mapping from the object literal of the rdfa:prefix predicate to the
238
+ # object literal of the rdfa:uri predicate. Add or update this mapping in the local list of
239
+ # URI mappings after transforming the 'prefix' component to lower-case.
240
+ # For every extracted
241
+ um[prefix.to_s.downcase] = @graph.bind(Namespace.new(uri.to_s, prefix.to_s.downcase)) if prefix
242
+
243
+ # triple that is the common subject of an rdfa:term and an rdfa:uri predicate, create a
244
+ # mapping from the object literal of the rdfa:term predicate to the object literal of the
245
+ # rdfa:uri predicate. Add or update this mapping in the local term mappings.
246
+ tm[term.to_s] = URIRef.new(uri.to_s) if term
247
+ end
248
+ rescue ParserException
249
+ add_debug(element, "extract_mappings: profile subject #{subject.to_s}: #{e.message}")
250
+ raise if @strict
251
+ rescue RuntimeError => e
252
+ add_debug(element, "extract_mappings: profile: #{e.message}")
253
+ raise if @strict
254
+ end
255
+ end
256
+
257
+ # Merge mappings from this vocabulary
258
+ uri_mappings.merge!(@@vocabulary_cache[profile][:uri_mappings])
259
+ term_mappings.merge!(@@vocabulary_cache[profile][:term_mappings])
260
+ end
261
+
112
262
  # look for xmlns
113
- element.namespaces.each do |attr_name,attr_value|
263
+ # (note, this may be dependent on @host_language)
264
+ # Regardless of how the mapping is declared, the value to be mapped must be converted to lower case,
265
+ # and the URI is not processed in any way; in particular if it is a relative path it is
266
+ # not resolved against the current base.
267
+ element.namespaces.each do |attr_name, attr_value|
114
268
  begin
115
- abbr, suffix = attr_name.split(":")
116
- mappings[suffix] = @graph.bind(Namespace.new(attr_value, suffix)) if abbr == "xmlns"
269
+ abbr, prefix = attr_name.split(":")
270
+ uri_mappings[prefix.to_s.downcase] = @graph.bind(Namespace.new(attr_value, prefix.to_s.downcase)) if abbr.downcase == "xmlns" && prefix
117
271
  rescue RdfException => e
118
272
  add_debug(element, "extract_mappings raised #{e.class}: #{e.message}")
119
273
  raise if @strict
120
274
  end
121
275
  end
122
276
 
123
- add_debug(element, "mappings: #{mappings.keys.join(", ")}")
124
- mappings
277
+ # Set mappings from @prefix
278
+ # prefix is a whitespace separated list of prefix-name URI pairs of the form
279
+ # NCName ':' ' '+ xs:anyURI
280
+ # SPEC Confusion: prefix is forced to lower-case in @profile, but not specified here.
281
+ mappings = element.attributes["prefix"].to_s.split(/\s+/)
282
+ while mappings.length > 0 do
283
+ prefix, uri = mappings.shift.downcase, mappings.shift
284
+ #puts "uri_mappings prefix #{prefix} <#{uri}>"
285
+ next unless prefix.match(/:$/)
286
+ prefix.chop!
287
+
288
+ uri_mappings[prefix] = @graph.bind(Namespace.new(uri, prefix))
289
+ end
290
+
291
+ add_debug(element, "uri_mappings: #{uri_mappings.values.map{|ns|ns.to_s}.join(", ")}")
292
+ add_debug(element, "term_mappings: #{term_mappings.keys.join(", ")}")
125
293
  end
126
294
 
127
295
  # The recursive helper function
@@ -132,6 +300,8 @@ module RdfContext
132
300
  return nil
133
301
  end
134
302
 
303
+ add_debug(element, "traverse, ec: #{evaluation_context.inspect}")
304
+
135
305
  # local variables [5.5 Step 1]
136
306
  recurse = true
137
307
  skip = false
@@ -140,6 +310,10 @@ module RdfContext
140
310
  uri_mappings = evaluation_context.uri_mappings.clone
141
311
  incomplete_triples = []
142
312
  language = evaluation_context.language
313
+ term_mappings = evaluation_context.term_mappings.clone
314
+ default_vocabulary = evaluation_context.default_vocabulary
315
+
316
+ current_object_literal = nil # XXX Not explicit
143
317
 
144
318
  # shortcut
145
319
  attrs = element.attributes
@@ -148,6 +322,7 @@ module RdfContext
148
322
  src = attrs['src']
149
323
  resource = attrs['resource']
150
324
  href = attrs['href']
325
+ vocab = attrs['vocab']
151
326
 
152
327
  # Pull out the attributes needed for the skip test.
153
328
  property = attrs['property'].to_s if attrs['property']
@@ -157,42 +332,73 @@ module RdfContext
157
332
  rel = attrs['rel'].to_s if attrs['rel']
158
333
  rev = attrs['rev'].to_s if attrs['rev']
159
334
 
160
- # SPEC CONFUSION: not sure what to initialize this value to
161
- current_object_literal = nil
162
-
163
- # XMLNS mappings [5.5 Step 2]
164
- uri_mappings.merge!(extract_mappings(element))
335
+ # Default vocabulary [7.5 Step 2]
336
+ # First the current element is examined for any change to the default vocabulary via @vocab.
337
+ # If @vocab is present and contains a value, its value updates the local default vocabulary.
338
+ # If the value is empty, then the local default vocabulary must be reset to the Host Language defined default.
339
+ unless vocab.nil?
340
+ default_vocabulary = if vocab.to_s.empty?
341
+ # Set default_vocabulary to host language default
342
+ @host_defaults.fetch(:voabulary, nil)
343
+ else
344
+ vocab.to_s
345
+ end
346
+ add_debug(element, "[Step 2] traverse, default_vocaulary: #{default_vocabulary.inspect}")
347
+ end
348
+
349
+ # Local term mappings [7.5 Steps 3 & 4]
350
+ # Next the current element is parsed for any updates to the local term mappings and local list of URI mappings via @profile.
351
+ # If @profile is present, its value is processed as defined in RDFa Profiles.
352
+ extract_mappings(element, uri_mappings, term_mappings)
165
353
 
166
- # Language information [5.5 Step 3]
167
- add_debug(element, "traverse, lang: #{attrs['lang']}") if attrs['lang']
168
- language = attrs['lang'] || language
354
+ # Language information [7.5 Step 5]
355
+ # From HTML5 [3.2.3.3]
356
+ # If both the lang attribute in no namespace and the lang attribute in the XML namespace are set
357
+ # on an element, user agents must use the lang attribute in the XML namespace, and the lang
358
+ # attribute in no namespace must be ignored for the purposes of determining the element's
359
+ # language.
360
+ language = case
361
+ when element.at_xpath("@xml:lang", "xml" => XML_NS.uri.to_s)
362
+ element.at_xpath("@xml:lang", "xml" => XML_NS.uri.to_s).to_s
363
+ when element.at_xpath("lang")
364
+ element.at_xpath("lang").to_s
365
+ else
366
+ language
367
+ end
368
+ add_debug(element, "HTML5 [3.2.3.3] traverse, lang: #{language}") if attrs['lang']
169
369
 
170
370
  # rels and revs
171
- rels = parse_curies(rel, uri_mappings, evaluation_context.base, true)
172
- revs = parse_curies(rev, uri_mappings, evaluation_context.base, true)
173
- valid_rel_or_rev = !(rel.nil? && rev.nil?)
371
+ rels = process_uris(element, rel, evaluation_context, :uri_mappings => uri_mappings, :term_mappings => term_mappings, :vocab => default_vocabulary)
372
+ revs = process_uris(element, rev, evaluation_context, :uri_mappings => uri_mappings, :term_mappings => term_mappings, :vocab => default_vocabulary)
174
373
 
175
- add_debug(element, "traverse, ec: #{evaluation_context.inspect}")
176
374
  add_debug(element, "traverse, about: #{about.nil? ? 'nil' : about}, src: #{src.nil? ? 'nil' : src}, resource: #{resource.nil? ? 'nil' : resource}, href: #{href.nil? ? 'nil' : href}")
177
375
  add_debug(element, "traverse, property: #{property.nil? ? 'nil' : property}, typeof: #{typeof.nil? ? 'nil' : typeof}, datatype: #{datatype.nil? ? 'nil' : datatype}, content: #{content.nil? ? 'nil' : content}")
178
376
  add_debug(element, "traverse, rels: #{rels.join(" ")}, revs: #{revs.join(" ")}")
179
377
 
180
- if not valid_rel_or_rev
181
- # Establishing a new subject if no valid rel/rev [5.5 Step 4]
378
+ if !(rel || rev)
379
+ # Establishing a new subject if no rel/rev [7.5 Step 6]
380
+ # May not be valid, but can exist
182
381
  if about
183
- new_subject = uri_or_safe_curie(about, evaluation_context, uri_mappings)
382
+ new_subject = process_uri(element, about, evaluation_context, :uri_mappings => uri_mappings)
184
383
  elsif src
185
- new_subject = URIRef.new(src, evaluation_context.base)
384
+ new_subject = process_uri(element, src, evaluation_context)
186
385
  elsif resource
187
- new_subject = uri_or_safe_curie(resource, evaluation_context, uri_mappings)
386
+ new_subject = process_uri(element, resource, evaluation_context, :uri_mappings => uri_mappings)
188
387
  elsif href
189
- new_subject = URIRef.new(href, evaluation_context.base)
388
+ new_subject = process_uri(element, href, evaluation_context)
190
389
  end
191
390
 
192
- # SPEC CONFUSION: not sure what "If no URI is provided by a resource attribute" means, I assume
193
- # it means that new_subject is still null
391
+ # If no URI is provided by a resource attribute, then the first match from the following rules
392
+ # will apply:
393
+ # if @typeof is present, then new subject is set to be a newly created bnode.
394
+ # otherwise,
395
+ # if parent object is present, new subject is set to the value of parent object.
396
+ # Additionally, if @property is not present then the skip element flag is set to 'true';
194
397
  if new_subject.nil?
195
- if element.name =~ /^(head|body)$/ && evaluation_context.base
398
+ if @host_language == :xhtml && element.name =~ /^(head|body)$/ && evaluation_context.base
399
+ # From XHTML+RDFa 1.1:
400
+ # if no URI is provided, then first check to see if the element is the head or body element.
401
+ # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
196
402
  new_subject = URIRef.new(evaluation_context.base)
197
403
  elsif element.attributes['typeof']
198
404
  new_subject = BNode.new
@@ -202,19 +408,23 @@ module RdfContext
202
408
  skip = true unless property
203
409
  end
204
410
  end
205
- add_debug(element, "new_subject: #{new_subject}, skip = #{skip}")
411
+ add_debug(element, "[Step 6] new_subject: #{new_subject}, skip = #{skip}")
206
412
  else
207
- # Establish both new subject and current object resource [5.5 Step 5]
208
-
413
+ # [7.5 Step 7]
414
+ # If the current element does contain a @rel or @rev attribute, then the next step is to
415
+ # establish both a value for new subject and a value for current object resource:
209
416
  if about
210
- new_subject = uri_or_safe_curie(about, evaluation_context, uri_mappings)
417
+ new_subject = process_uri(element, about, evaluation_context, :uri_mappings => uri_mappings)
211
418
  elsif src
212
- new_subject = uri_or_safe_curie(src, evaluation_context, uri_mappings)
419
+ new_subject = process_uri(element, src, evaluation_context, :uri_mappings => uri_mappings)
213
420
  end
214
421
 
215
422
  # If no URI is provided then the first match from the following rules will apply
216
423
  if new_subject.nil?
217
- if element.name =~ /^(head|body)$/
424
+ if @host_language == :xhtml && element.name =~ /^(head|body)$/
425
+ # From XHTML+RDFa 1.1:
426
+ # if no URI is provided, then first check to see if the element is the head or body element.
427
+ # If it is, then act as if there is an empty @about present, and process it according to the rule for @about.
218
428
  new_subject = URIRef.new(evaluation_context.base)
219
429
  elsif element.attributes['typeof']
220
430
  new_subject = BNode.new
@@ -225,90 +435,85 @@ module RdfContext
225
435
  end
226
436
  end
227
437
 
438
+ # Then the current object resource is set to the URI obtained from the first match from the following rules:
228
439
  if resource
229
- current_object_resource = uri_or_safe_curie(resource, evaluation_context, uri_mappings)
440
+ current_object_resource = process_uri(element, resource, evaluation_context, :uri_mappings => uri_mappings)
230
441
  elsif href
231
- current_object_resource = URIRef.new(href, evaluation_context.base)
442
+ current_object_resource = process_uri(element, href, evaluation_context)
232
443
  end
233
444
 
234
- add_debug(element, "new_subject: #{new_subject}, current_object_resource = #{current_object_resource.nil? ? 'nil' : current_object_resource}")
445
+ add_debug(element, "[Step 7] new_subject: #{new_subject}, current_object_resource = #{current_object_resource.nil? ? 'nil' : current_object_resource}")
235
446
  end
236
447
 
237
- # Process @typeof if there is a subject [Step 6]
448
+ # Process @typeof if there is a subject [Step 8]
238
449
  if new_subject and typeof
239
- types = parse_curies(typeof, uri_mappings, evaluation_context.base, false)
450
+ # Typeof is TERMorCURIEorURIs
451
+ types = process_uris(element, typeof, evaluation_context, :uri_mappings => uri_mappings, :term_mappings => term_mappings, :vocab => default_vocabulary)
240
452
  add_debug(element, "typeof: #{typeof}")
241
453
  types.each do |one_type|
242
454
  add_triple(element, new_subject, RDF_TYPE, one_type)
243
455
  end
244
456
  end
245
457
 
246
- # Generate triples with given object [Step 7]
458
+ # Generate triples with given object [Step 9]
247
459
  if current_object_resource
248
- rels.each do |rel|
249
- add_triple(element, new_subject, rel, current_object_resource)
460
+ rels.each do |r|
461
+ add_triple(element, new_subject, r, current_object_resource)
250
462
  end
251
463
 
252
- revs.each do |rev|
253
- add_triple(element, current_object_resource, rev, new_subject)
464
+ revs.each do |r|
465
+ add_triple(element, current_object_resource, r, new_subject)
254
466
  end
255
- else
256
- # Incomplete triples and bnode creation [Step 8]
257
- add_debug(element, "step 8: valid: #{valid_rel_or_rev}, rels: #{rels}, revs: #{revs}")
258
- current_object_resource = BNode.new if valid_rel_or_rev
467
+ elsif rel || rev
468
+ # Incomplete triples and bnode creation [Step 10]
469
+ add_debug(element, "[Step 10] incompletes: rels: #{rels}, revs: #{revs}")
470
+ current_object_resource = BNode.new
259
471
 
260
- rels.each do |rel|
261
- # SPEC CONFUSION: we don't store the subject here?
262
- incomplete_triples << {:predicate => rel, :direction => :forward}
472
+ rels.each do |r|
473
+ incomplete_triples << {:predicate => r, :direction => :forward}
263
474
  end
264
475
 
265
- revs.each do |rev|
266
- # SPEC CONFUSION: we don't store the object here?
267
- incomplete_triples << {:predicate => rev, :direction => :reverse}
476
+ revs.each do |r|
477
+ incomplete_triples << {:predicate => r, :direction => :reverse}
268
478
  end
269
-
270
479
  end
271
480
 
272
- # Establish current object literal [Step 9]
481
+ # Establish current object literal [Step 11]
273
482
  if property
274
- properties = parse_curies(property, uri_mappings, evaluation_context.base, false)
483
+ properties = process_uris(element, property, evaluation_context, :uri_mappings => uri_mappings, :term_mappings => term_mappings, :vocab => default_vocabulary)
275
484
 
276
485
  # get the literal datatype
277
486
  type = datatype
278
487
  children_node_types = element.children.collect{|c| c.class}.uniq
279
488
 
280
489
  # the following 3 IF clauses should be mutually exclusive. Written as is to prevent extensive indentation.
281
-
282
- # SPEC CONFUSION: have to special case XML Literal, not clear right away.
283
- # SPEC CONFUSION: specify that the conditions are in order of priority
284
- type_resource = curie_to_resource_or_bnode(type, uri_mappings, evaluation_context.base) if type
490
+ type_resource = process_uri(element, type, evaluation_context, :uri_mappings => uri_mappings, :term_mappings => term_mappings, :vocab => default_vocabulary) if type
285
491
  if type and !type.empty? and (type_resource.to_s != XML_LITERAL.to_s)
286
492
  # typed literal
287
- add_debug(element, "typed literal")
493
+ add_debug(element, "[Step 11] typed literal")
288
494
  current_object_literal = Literal.typed(content || element.inner_text, type_resource, :language => language)
289
495
  elsif content or (children_node_types == [Nokogiri::XML::Text]) or (element.children.length == 0) or (type == '')
290
496
  # plain literal
291
- add_debug(element, "plain literal")
497
+ add_debug(element, "[Step 11] plain literal")
292
498
  current_object_literal = Literal.untyped(content || element.inner_text, language)
293
499
  elsif children_node_types != [Nokogiri::XML::Text] and (type == nil or type_resource.to_s == XML_LITERAL.to_s)
294
500
  # XML Literal
295
- add_debug(element, "XML Literal: #{element.inner_html}")
501
+ add_debug(element, "[Step 11] XML Literal: #{element.inner_html}")
296
502
  current_object_literal = Literal.typed(element.children, XML_LITERAL, :language => language, :namespaces => uri_mappings)
297
503
  recurse = false
298
504
  end
299
505
 
300
506
  # add each property
301
- properties.each do |property|
302
- add_triple(element, new_subject, property, current_object_literal)
507
+ properties.each do |p|
508
+ add_triple(element, new_subject, p, current_object_literal)
303
509
  end
304
-
305
510
  # SPEC CONFUSION: "the triple has been created" ==> there may be more than one
306
511
  # set the recurse flag above in the IF about xmlliteral, as it is the only place that can happen
307
512
  end
308
513
 
309
- # Complete the incomplete triples from the evaluation context [Step 10]
310
- add_debug(element, "10: skip=#{skip}, new_subject=#{new_subject}")
311
- if not skip and new_subject
514
+ if not skip and new_subject && !evaluation_context.incomplete_triples.empty?
515
+ # Complete the incomplete triples from the evaluation context [Step 12]
516
+ add_debug(element, "[Step 12] complete incomplete triples: new_subject=#{new_subject}, completes=#{evaluation_context.incomplete_triples.inspect}")
312
517
  evaluation_context.incomplete_triples.each do |trip|
313
518
  if trip[:direction] == :forward
314
519
  add_triple(element, evaluation_context.parent_subject, trip[:predicate], new_subject)
@@ -317,26 +522,35 @@ module RdfContext
317
522
  end
318
523
  end
319
524
  end
320
-
321
- # Create a new evaluation context and proceed recursively [Step 11]
322
- if recurse
323
- # SPEC CONFUSION: new evaluation context for each child? Probably not necessary,
324
- # but maybe needs to be pointed out?
325
525
 
526
+ # Create a new evaluation context and proceed recursively [Step 13]
527
+ if recurse
326
528
  if skip
327
- new_ec = evaluation_context.clone
328
- new_ec.language = language
329
- new_ec.uri_mappings = uri_mappings
330
- add_debug(element, "skip: cloned ec: #{evaluation_context.inspect}")
529
+ if language == evaluation_context.language &&
530
+ uri_mappings == evaluation_context.uri_mappings &&
531
+ term_mappings == evaluation_context.term_mappings &&
532
+ default_vocabulary == evaluation_context.default_vocabulary &&
533
+ new_ec = evaluation_context
534
+ add_debug(element, "[Step 13] skip: reused ec")
535
+ else
536
+ new_ec = evaluation_context.clone
537
+ new_ec.language = language
538
+ new_ec.uri_mappings = uri_mappings
539
+ new_ec.term_mappings = term_mappings
540
+ new_ec.default_vocabulary = default_vocabulary
541
+ add_debug(element, "[Step 13] skip: cloned ec")
542
+ end
331
543
  else
332
544
  # create a new evaluation context
333
- new_ec = EvaluationContext.new(evaluation_context.base)
545
+ new_ec = EvaluationContext.new(evaluation_context.base, @host_defaults)
334
546
  new_ec.parent_subject = new_subject || evaluation_context.parent_subject
335
547
  new_ec.parent_object = current_object_resource || new_subject || evaluation_context.parent_subject
336
548
  new_ec.uri_mappings = uri_mappings
337
549
  new_ec.incomplete_triples = incomplete_triples
338
550
  new_ec.language = language
339
- #add_debug(element, "new ec: #{new_ec.inspect}")
551
+ new_ec.term_mappings = term_mappings
552
+ new_ec.default_vocabulary = default_vocabulary
553
+ add_debug(element, "[Step 13] new ec")
340
554
  end
341
555
 
342
556
  element.children.each do |child|
@@ -345,57 +559,97 @@ module RdfContext
345
559
  end
346
560
  end
347
561
  end
348
-
349
- # space-separated CURIEs or Link Types
350
- def parse_curies(value, uri_mappings, base, with_link_types=false)
351
- return [] unless value
352
- resource_array = []
353
- value.to_s.split(' ').each do |curie|
354
- if curie.include?(":")
355
- resource_array << curie_to_resource_or_bnode(curie, uri_mappings, base)
356
- elsif with_link_types
357
- # Reserved words are all mapped to lower case
358
- curie = curie.to_s.downcase
359
- link_type_curie = curie_to_resource_or_bnode(":#{curie}", XH_MAPPING, base) if LINK_TYPES.include?(curie)
360
- resource_array << link_type_curie if link_type_curie
562
+
563
+ # space-separated TERMorCURIEorURI
564
+ def process_uris(element, value, evaluation_context, options)
565
+ return [] if value.to_s.empty?
566
+ add_debug(element, "process_uris: #{value}")
567
+ value.to_s.split(/\s+/).map {|v| process_uri(element, v, evaluation_context, options)}.compact
568
+ end
569
+
570
+ def process_uri(element, value, evaluation_context, options = {})
571
+ #return if value.to_s.empty?
572
+ #add_debug(element, "process_uri: #{value}")
573
+ options = {:uri_mappings => {}}.merge(options)
574
+ if !options[:term_mappings] && options[:uri_mappings] && value.to_s.match(/^\[(.*)\]$/)
575
+ # SafeCURIEorCURIEorURI
576
+ # When the value is surrounded by square brackets, then the content within the brackets is
577
+ # evaluated as a CURIE according to the CURIE Syntax definition. If it is not a valid CURIE, the
578
+ # value must be ignored.
579
+ uri = curie_to_resource_or_bnode(element, $1, options[:uri_mappings], evaluation_context.parent_subject)
580
+ add_debug(element, "process_uri: #{value} => safeCURIE => <#{uri}>")
581
+ uri
582
+ elsif options[:term_mappings] && NC_REGEXP.match(value.to_s)
583
+ # TERMorCURIEorURI
584
+ # If the value is an NCName, then it is evaluated as a term according to General Use of Terms in
585
+ # Attributes. Note that this step may mean that the value is to be ignored.
586
+ uri = process_term(value.to_s, options)
587
+ add_debug(element, "process_uri: #{value} => term => <#{uri}>")
588
+ uri
589
+ else
590
+ # SafeCURIEorCURIEorURI or TERMorCURIEorURI
591
+ # Otherwise, the value is evaluated as a CURIE.
592
+ # If it is a valid CURIE, the resulting URI is used; otherwise, the value will be processed as a URI.
593
+ uri = curie_to_resource_or_bnode(element, value, options[:uri_mappings], evaluation_context.parent_subject)
594
+ if uri
595
+ add_debug(element, "process_uri: #{value} => CURIE => <#{uri}>")
596
+ else
597
+ uri = URIRef.new(value, evaluation_context.base)
598
+ add_debug(element, "process_uri: #{value} => URI => <#{uri}>")
361
599
  end
600
+ uri
601
+ end
602
+ end
603
+
604
+ # [7.4.3] General Use of Terms in Attributes
605
+ #
606
+ # @param [String] term:: term
607
+ # @param [Hash] options:: Parser options, one of
608
+ # <em>options[:term_mappings]</em>:: Term mappings
609
+ # <em>options[:vocab]</em>:: Default vocabulary
610
+ def process_term(value, options)
611
+ case
612
+ when options[:term_mappings].is_a?(Hash) && options[:term_mappings].has_key?(value.to_s.downcase)
613
+ # If the term is in the local term mappings, use the associated URI.
614
+ # XXX Spec Confusion: are terms always downcased? Or only for XHTML Vocab?
615
+ options[:term_mappings][value.to_s.downcase]
616
+ when options[:vocab]
617
+ # Otherwise, if there is a local default vocabulary the URI is obtained by concatenating that value and the term.
618
+ options[:vocab] + value
619
+ else
620
+ # Finally, if there is no local default vocabulary, the term has no associated URI and must be ignored.
621
+ nil
362
622
  end
363
- resource_array
364
623
  end
365
624
 
366
- def curie_to_resource_or_bnode(curie, uri_mappings, subject)
625
+ # From section 6. CURIE Syntax Definition
626
+ def curie_to_resource_or_bnode(element, curie, uri_mappings, subject)
367
627
  # URI mappings for CURIEs default to XH_MAPPING, rather than the default doc namespace
368
- uri_mappings = uri_mappings.merge(XH_MAPPING)
369
- prefix, suffix = curie.to_s.split(":")
628
+ prefix, reference = curie.to_s.split(":")
370
629
 
371
630
  # consider the bnode situation
372
631
  if prefix == "_"
373
632
  # we force a non-nil name, otherwise it generates a new name
374
- BNode.new(suffix || "", @named_bnodes)
375
- elsif curie.to_s.empty?
376
- add_debug(nil, "curie_to_resource_or_bnode #{subject}")
377
- # Empty curie resolves to current subject (No, an empty curie should be ignored)
378
- # URIRef.new(subject)
633
+ BNode.new(reference || "", @named_bnodes)
634
+ elsif curie.to_s.match(/^:/)
635
+ # Default prefix
636
+ if uri_mappings[""]
637
+ uri_mappings[""].send("#{reference}_")
638
+ elsif @host_defaults[:prefix]
639
+ @host_defaults[:prefix].send("#{reference}_")
640
+ end
641
+ elsif !curie.to_s.match(/:/)
642
+ # No prefix, undefined (in this context, it is evaluated as a term elsewhere)
379
643
  nil
380
644
  else
381
- ns = uri_mappings[prefix.to_s]
382
- unless ns
383
- add_debug(nil, "curie_to_resource_or_bnode No namespace mapping for #{prefix}")
384
- raise ParserException, "No namespace mapping for #{prefix}" if @strict
385
- return nil
645
+ # XXX Spec Confusion, are prefixes always downcased?
646
+ ns = uri_mappings[prefix.to_s.downcase]
647
+ if ns
648
+ ns + reference
649
+ else
650
+ add_debug(element, "curie_to_resource_or_bnode No namespace mapping for #{prefix.downcase}")
651
+ nil
386
652
  end
387
- ns + suffix
388
- end
389
- end
390
-
391
- def uri_or_safe_curie(value, evaluation_context, uri_mappings)
392
- return nil if value.nil?
393
-
394
- # check if the value is [foo:bar]
395
- if value.to_s.match(/^\[(.*)\]$/)
396
- curie_to_resource_or_bnode($1, uri_mappings, evaluation_context.parent_subject)
397
- else
398
- URIRef.new(value, evaluation_context.base)
399
653
  end
400
654
  end
401
655
  end