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 +12 -0
- data/VERSION +1 -1
- data/bin/rdf_context +48 -3
- data/lib/rdf_context.rb +4 -1
- data/lib/rdf_context/exceptions.rb +1 -0
- data/lib/rdf_context/graph.rb +6 -1
- data/lib/rdf_context/namespace.rb +11 -15
- data/lib/rdf_context/parser.rb +1 -1
- data/lib/rdf_context/rdfaparser.rb +385 -131
- data/lib/rdf_context/serializer/recursive_serializer.rb +3 -7
- data/lib/rdf_context/serializer/xml_serializer.rb +21 -8
- data/lib/rdf_context/store/abstract_store.rb +0 -1
- data/lib/rdf_context/uriref.rb +25 -19
- data/spec/graph_spec.rb +16 -7
- data/spec/literal_spec.rb +10 -10
- data/spec/matchers.rb +1 -0
- data/spec/n3parser_spec.rb +2 -2
- data/spec/namespaces_spec.rb +16 -2
- data/spec/rdfa_helper.rb +8 -13
- data/spec/rdfa_parser_spec.rb +50 -6
- data/spec/spec_helper.rb +5 -0
- data/spec/turtle_serializer_spec.rb +6 -7
- data/spec/uriref_spec.rb +11 -27
- metadata +3 -5
- data/cmx.db +0 -0
- data/ep.nt +0 -194
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.
|
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/
|
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
|
data/lib/rdf_context/graph.rb
CHANGED
@@ -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
|
19
|
+
def initialize(uri, prefix)
|
21
20
|
prefix = prefix.to_s
|
22
21
|
|
23
|
-
@
|
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 [
|
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
|
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
|
-
|
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
|
data/lib/rdf_context/parser.rb
CHANGED
@@ -7,7 +7,7 @@ module RdfContext
|
|
7
7
|
attr_accessor :doc, :graph
|
8
8
|
|
9
9
|
##
|
10
|
-
# Creates a new parser
|
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:
|
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
|
-
|
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
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
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
|
-
|
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,
|
116
|
-
|
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
|
-
|
124
|
-
|
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
|
-
#
|
161
|
-
|
162
|
-
|
163
|
-
#
|
164
|
-
|
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 [
|
167
|
-
|
168
|
-
|
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 =
|
172
|
-
revs =
|
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
|
181
|
-
# Establishing a new subject if no
|
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 =
|
382
|
+
new_subject = process_uri(element, about, evaluation_context, :uri_mappings => uri_mappings)
|
184
383
|
elsif src
|
185
|
-
new_subject =
|
384
|
+
new_subject = process_uri(element, src, evaluation_context)
|
186
385
|
elsif resource
|
187
|
-
new_subject =
|
386
|
+
new_subject = process_uri(element, resource, evaluation_context, :uri_mappings => uri_mappings)
|
188
387
|
elsif href
|
189
|
-
new_subject =
|
388
|
+
new_subject = process_uri(element, href, evaluation_context)
|
190
389
|
end
|
191
390
|
|
192
|
-
#
|
193
|
-
#
|
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
|
-
#
|
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 =
|
417
|
+
new_subject = process_uri(element, about, evaluation_context, :uri_mappings => uri_mappings)
|
211
418
|
elsif src
|
212
|
-
new_subject =
|
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 =
|
440
|
+
current_object_resource = process_uri(element, resource, evaluation_context, :uri_mappings => uri_mappings)
|
230
441
|
elsif href
|
231
|
-
current_object_resource =
|
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
|
448
|
+
# Process @typeof if there is a subject [Step 8]
|
238
449
|
if new_subject and typeof
|
239
|
-
|
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
|
458
|
+
# Generate triples with given object [Step 9]
|
247
459
|
if current_object_resource
|
248
|
-
rels.each do |
|
249
|
-
add_triple(element, new_subject,
|
460
|
+
rels.each do |r|
|
461
|
+
add_triple(element, new_subject, r, current_object_resource)
|
250
462
|
end
|
251
463
|
|
252
|
-
revs.each do |
|
253
|
-
add_triple(element, current_object_resource,
|
464
|
+
revs.each do |r|
|
465
|
+
add_triple(element, current_object_resource, r, new_subject)
|
254
466
|
end
|
255
|
-
|
256
|
-
# Incomplete triples and bnode creation [Step
|
257
|
-
add_debug(element, "
|
258
|
-
current_object_resource = BNode.new
|
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 |
|
261
|
-
|
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 |
|
266
|
-
|
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
|
481
|
+
# Establish current object literal [Step 11]
|
273
482
|
if property
|
274
|
-
properties =
|
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 |
|
302
|
-
add_triple(element, new_subject,
|
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
|
-
|
310
|
-
|
311
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
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
|
-
|
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
|
350
|
-
def
|
351
|
-
return []
|
352
|
-
|
353
|
-
value.to_s.split(
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
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
|
-
|
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
|
-
|
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(
|
375
|
-
elsif curie.to_s.
|
376
|
-
|
377
|
-
|
378
|
-
|
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
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
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
|