rdf_context 0.5.3 → 0.5.5

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.
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