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