openlogic-rdf 0.3.6
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/AUTHORS +3 -0
- data/CREDITS +9 -0
- data/README +361 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/bin/rdf +18 -0
- data/etc/doap.nt +62 -0
- data/lib/df.rb +1 -0
- data/lib/rdf/cli.rb +200 -0
- data/lib/rdf/format.rb +383 -0
- data/lib/rdf/mixin/countable.rb +39 -0
- data/lib/rdf/mixin/durable.rb +31 -0
- data/lib/rdf/mixin/enumerable.rb +637 -0
- data/lib/rdf/mixin/indexable.rb +26 -0
- data/lib/rdf/mixin/inferable.rb +5 -0
- data/lib/rdf/mixin/mutable.rb +191 -0
- data/lib/rdf/mixin/queryable.rb +265 -0
- data/lib/rdf/mixin/readable.rb +15 -0
- data/lib/rdf/mixin/type_check.rb +21 -0
- data/lib/rdf/mixin/writable.rb +152 -0
- data/lib/rdf/model/graph.rb +263 -0
- data/lib/rdf/model/list.rb +731 -0
- data/lib/rdf/model/literal/boolean.rb +121 -0
- data/lib/rdf/model/literal/date.rb +73 -0
- data/lib/rdf/model/literal/datetime.rb +72 -0
- data/lib/rdf/model/literal/decimal.rb +86 -0
- data/lib/rdf/model/literal/double.rb +189 -0
- data/lib/rdf/model/literal/integer.rb +126 -0
- data/lib/rdf/model/literal/numeric.rb +184 -0
- data/lib/rdf/model/literal/time.rb +87 -0
- data/lib/rdf/model/literal/token.rb +47 -0
- data/lib/rdf/model/literal/xml.rb +39 -0
- data/lib/rdf/model/literal.rb +373 -0
- data/lib/rdf/model/node.rb +156 -0
- data/lib/rdf/model/resource.rb +28 -0
- data/lib/rdf/model/statement.rb +296 -0
- data/lib/rdf/model/term.rb +77 -0
- data/lib/rdf/model/uri.rb +570 -0
- data/lib/rdf/model/value.rb +133 -0
- data/lib/rdf/nquads.rb +152 -0
- data/lib/rdf/ntriples/format.rb +48 -0
- data/lib/rdf/ntriples/reader.rb +239 -0
- data/lib/rdf/ntriples/writer.rb +219 -0
- data/lib/rdf/ntriples.rb +104 -0
- data/lib/rdf/query/pattern.rb +329 -0
- data/lib/rdf/query/solution.rb +252 -0
- data/lib/rdf/query/solutions.rb +237 -0
- data/lib/rdf/query/variable.rb +221 -0
- data/lib/rdf/query.rb +404 -0
- data/lib/rdf/reader.rb +511 -0
- data/lib/rdf/repository.rb +389 -0
- data/lib/rdf/transaction.rb +161 -0
- data/lib/rdf/util/aliasing.rb +63 -0
- data/lib/rdf/util/cache.rb +139 -0
- data/lib/rdf/util/file.rb +38 -0
- data/lib/rdf/util/uuid.rb +36 -0
- data/lib/rdf/util.rb +6 -0
- data/lib/rdf/version.rb +19 -0
- data/lib/rdf/vocab/cc.rb +18 -0
- data/lib/rdf/vocab/cert.rb +13 -0
- data/lib/rdf/vocab/dc.rb +63 -0
- data/lib/rdf/vocab/dc11.rb +23 -0
- data/lib/rdf/vocab/doap.rb +45 -0
- data/lib/rdf/vocab/exif.rb +168 -0
- data/lib/rdf/vocab/foaf.rb +69 -0
- data/lib/rdf/vocab/geo.rb +13 -0
- data/lib/rdf/vocab/http.rb +26 -0
- data/lib/rdf/vocab/owl.rb +59 -0
- data/lib/rdf/vocab/rdfs.rb +17 -0
- data/lib/rdf/vocab/rsa.rb +12 -0
- data/lib/rdf/vocab/rss.rb +14 -0
- data/lib/rdf/vocab/sioc.rb +93 -0
- data/lib/rdf/vocab/skos.rb +36 -0
- data/lib/rdf/vocab/wot.rb +21 -0
- data/lib/rdf/vocab/xhtml.rb +9 -0
- data/lib/rdf/vocab/xsd.rb +58 -0
- data/lib/rdf/vocab.rb +215 -0
- data/lib/rdf/writer.rb +475 -0
- data/lib/rdf.rb +192 -0
- metadata +173 -0
data/lib/rdf/nquads.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
module RDF
|
2
|
+
##
|
3
|
+
# **`RDF::NQuads`** provides support for the N-Quads serialization format.
|
4
|
+
#
|
5
|
+
# This has not yet been implemented as of RDF.rb 0.3.x.
|
6
|
+
module NQuads
|
7
|
+
include NTriples
|
8
|
+
|
9
|
+
##
|
10
|
+
# N-Quads format specification.
|
11
|
+
#
|
12
|
+
# @example Obtaining an NQuads format class
|
13
|
+
# RDF::Format.for(:nquads) #=> RDF::NQuads::Format
|
14
|
+
# RDF::Format.for("etc/doap.nq")
|
15
|
+
# RDF::Format.for(:file_name => "etc/doap.nq")
|
16
|
+
# RDF::Format.for(:file_extension => "nq")
|
17
|
+
# RDF::Format.for(:content_type => "text/x-nquads")
|
18
|
+
#
|
19
|
+
# @see http://sw.deri.org/2008/07/n-quads/#mediatype
|
20
|
+
# @since 0.4.0
|
21
|
+
class Format < RDF::Format
|
22
|
+
content_type 'text/x-nquads', :extension => :nq
|
23
|
+
content_encoding 'utf-8'
|
24
|
+
|
25
|
+
reader { RDF::NQuads::Reader }
|
26
|
+
writer { RDF::NQuads::Writer }
|
27
|
+
|
28
|
+
##
|
29
|
+
# Sample detection to see if it matches N-Quads (or N-Triples)
|
30
|
+
#
|
31
|
+
# Use a text sample to detect the format of an input file. Sub-classes implement
|
32
|
+
# a matcher sufficient to detect probably format matches, including disambiguating
|
33
|
+
# between other similar formats.
|
34
|
+
#
|
35
|
+
# @param [String] sample Beginning several bytes (about 1K) of input.
|
36
|
+
# @return [Boolean]
|
37
|
+
def self.detect(sample)
|
38
|
+
!!sample.match(%r(
|
39
|
+
(?:\s*(?:<[^>]*>) | (?:_:\w+)) # Subject
|
40
|
+
(?:\s*<[^>]*>) # Predicate
|
41
|
+
\s*
|
42
|
+
(?:(?:<[^>]*>) | (?:_:\w+) | (?:"[^"\n]*"(?:^^|@\S+)?)) # Object
|
43
|
+
(?:\s*<[^>]*>) # Context
|
44
|
+
\s*\.
|
45
|
+
)mx) && !(
|
46
|
+
sample.match(%r(@(base|prefix|keywords)|\{)) || # Not Turtle/N3/TriG
|
47
|
+
sample.match(%r(<(html|rdf))i) # Not HTML or XML
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Reader < NTriples::Reader
|
53
|
+
##
|
54
|
+
# @param [String] input
|
55
|
+
# @return [RDF::Term]
|
56
|
+
# @since 0.4.0
|
57
|
+
def self.parse_context(input)
|
58
|
+
parse_uri(input) || parse_node(input) || parse_literal(input)
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Read a Quad, where the context is optional
|
63
|
+
#
|
64
|
+
# @return [Array]
|
65
|
+
# @see http://sw.deri.org/2008/07/n-quads/#grammar
|
66
|
+
# @since 0.4.0
|
67
|
+
def read_triple
|
68
|
+
loop do
|
69
|
+
readline.strip! # EOFError thrown on end of input
|
70
|
+
line = @line # for backtracking input in case of parse error
|
71
|
+
|
72
|
+
begin
|
73
|
+
unless blank? || read_comment
|
74
|
+
subject = read_uriref || read_node || fail_subject
|
75
|
+
predicate = read_uriref(:intern => true) || fail_predicate
|
76
|
+
object = read_uriref || read_node || read_literal || fail_object
|
77
|
+
context = read_uriref || read_node || read_literal
|
78
|
+
return [subject, predicate, object, {:context => context}]
|
79
|
+
end
|
80
|
+
rescue RDF::ReaderError => e
|
81
|
+
@line = line # this allows #read_value to work
|
82
|
+
raise e
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end # Reader
|
88
|
+
|
89
|
+
class Writer < NTriples::Writer
|
90
|
+
##
|
91
|
+
# @param [RDF::Statement] statement
|
92
|
+
# @return [void] `self`
|
93
|
+
def write_statement(statement)
|
94
|
+
write_quad(*statement.to_quad)
|
95
|
+
self
|
96
|
+
end
|
97
|
+
alias_method :insert_statement, :write_statement # support the RDF::Writable interface
|
98
|
+
|
99
|
+
##
|
100
|
+
# Outputs the N-Quads representation of a statement.
|
101
|
+
#
|
102
|
+
# @param [RDF::Resource] subject
|
103
|
+
# @param [RDF::URI] predicate
|
104
|
+
# @param [RDF::Term] object
|
105
|
+
# @return [void]
|
106
|
+
def write_quad(subject, predicate, object, context)
|
107
|
+
puts format_quad(subject, predicate, object, context)
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Returns the N-Quads representation of a statement.
|
112
|
+
#
|
113
|
+
# @param [RDF::Statement] statement
|
114
|
+
# @return [String]
|
115
|
+
# @since 0.4.0
|
116
|
+
def format_statement(statement)
|
117
|
+
format_quad(*statement.to_quad)
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Returns the N-Triples representation of a triple.
|
122
|
+
#
|
123
|
+
# @param [RDF::Resource] subject
|
124
|
+
# @param [RDF::URI] predicate
|
125
|
+
# @param [RDF::Term] object
|
126
|
+
# @param [RDF::Term] context
|
127
|
+
# @return [String]
|
128
|
+
def format_quad(subject, predicate, object, context)
|
129
|
+
s = "%s %s %s " % [subject, predicate, object].map { |value| format_term(value) }
|
130
|
+
s += format_term(context) + " " if context
|
131
|
+
s + "."
|
132
|
+
end
|
133
|
+
end # Writer
|
134
|
+
end # NQuads
|
135
|
+
|
136
|
+
|
137
|
+
##
|
138
|
+
# Extensions for `RDF::Value`.
|
139
|
+
module Value
|
140
|
+
##
|
141
|
+
# Returns the N-Triples representation of this value.
|
142
|
+
#
|
143
|
+
# This method is only available when the 'rdf/ntriples' serializer has
|
144
|
+
# been explicitly required.
|
145
|
+
#
|
146
|
+
# @return [String]
|
147
|
+
# @since 0.4.0
|
148
|
+
def to_quad
|
149
|
+
RDF::NQuads.serialize(self)
|
150
|
+
end
|
151
|
+
end # Value
|
152
|
+
end # RDF
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RDF::NTriples
|
2
|
+
##
|
3
|
+
# N-Triples format specification.
|
4
|
+
#
|
5
|
+
# Note: Latest standards activities treat N-Triples as a subset
|
6
|
+
# of Turtle. This includes text/ntriples+turtle mime type and a
|
7
|
+
# new default encoding of utf-8.
|
8
|
+
#
|
9
|
+
# @example Obtaining an NTriples format class
|
10
|
+
# RDF::Format.for(:ntriples) #=> RDF::NTriples::Format
|
11
|
+
# RDF::Format.for("etc/doap.nt")
|
12
|
+
# RDF::Format.for(:file_name => "etc/doap.nt")
|
13
|
+
# RDF::Format.for(:file_extension => "nt")
|
14
|
+
# RDF::Format.for(:content_type => "text/plain")
|
15
|
+
# RDF::Format.for(:content_type => "text/ntriples+turtle")
|
16
|
+
#
|
17
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntriples
|
18
|
+
class Format < RDF::Format
|
19
|
+
content_type 'text/plain', :extension => :nt, :alias => 'text/ntriples+turtle'
|
20
|
+
content_encoding 'utf-8'
|
21
|
+
|
22
|
+
reader { RDF::NTriples::Reader }
|
23
|
+
writer { RDF::NTriples::Writer }
|
24
|
+
|
25
|
+
##
|
26
|
+
# Sample detection to see if it matches N-Triples
|
27
|
+
#
|
28
|
+
# Use a text sample to detect the format of an input file. Sub-classes implement
|
29
|
+
# a matcher sufficient to detect probably format matches, including disambiguating
|
30
|
+
# between other similar formats.
|
31
|
+
#
|
32
|
+
# @param [String] sample Beginning several bytes (about 1K) of input.
|
33
|
+
# @return [Boolean]
|
34
|
+
def self.detect(sample)
|
35
|
+
!!sample.match(%r(
|
36
|
+
(?:(?:<[^>]*>) | (?:_:\w+)) # Subject
|
37
|
+
\s*
|
38
|
+
(?:<[^>]*>) # Predicate
|
39
|
+
\s*
|
40
|
+
(?:(?:<[^>]*>) | (?:_:\w+) | (?:"[^"\n]*"(?:^^|@\S+)?)) # Object
|
41
|
+
\s*\.
|
42
|
+
)mx) && !(
|
43
|
+
sample.match(%r(@(base|prefix|keywords)|\{)) || # Not Turtle/N3/TriG
|
44
|
+
sample.match(%r(<(html|rdf))i) # Not HTML or XML
|
45
|
+
) && !RDF::NQuads::Format.detect(sample)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
module RDF::NTriples
|
2
|
+
##
|
3
|
+
# N-Triples parser.
|
4
|
+
#
|
5
|
+
# @example Obtaining an NTriples reader class
|
6
|
+
# RDF::Reader.for(:ntriples) #=> RDF::NTriples::Reader
|
7
|
+
# RDF::Reader.for("etc/doap.nt")
|
8
|
+
# RDF::Reader.for(:file_name => "etc/doap.nt")
|
9
|
+
# RDF::Reader.for(:file_extension => "nt")
|
10
|
+
# RDF::Reader.for(:content_type => "text/plain")
|
11
|
+
#
|
12
|
+
# @example Parsing RDF statements from an NTriples file
|
13
|
+
# RDF::NTriples::Reader.open("etc/doap.nt") do |reader|
|
14
|
+
# reader.each_statement do |statement|
|
15
|
+
# puts statement.inspect
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example Parsing RDF statements from an NTriples string
|
20
|
+
# data = StringIO.new(File.read("etc/doap.nt"))
|
21
|
+
# RDF::NTriples::Reader.new(data) do |reader|
|
22
|
+
# reader.each_statement do |statement|
|
23
|
+
# puts statement.inspect
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntriples
|
28
|
+
class Reader < RDF::Reader
|
29
|
+
format RDF::NTriples::Format
|
30
|
+
|
31
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar
|
32
|
+
COMMENT = /^#\s*(.*)$/.freeze
|
33
|
+
NODEID = /^_:([A-Za-z][A-Za-z0-9\-_]*)/.freeze
|
34
|
+
URIREF = /^<([^>]+)>/.freeze
|
35
|
+
LITERAL_PLAIN = /^"((?:\\"|[^"])*)"/.freeze
|
36
|
+
LITERAL_WITH_LANGUAGE = /^"((?:\\"|[^"])*)"@([a-z]+[\-A-Za-z0-9]*)/.freeze
|
37
|
+
LITERAL_WITH_DATATYPE = /^"((?:\\"|[^"])*)"\^\^<([^>]+)>/.freeze
|
38
|
+
LANGUAGE_TAG = /^@([a-z]+[\-A-Za-z0-9]*)/.freeze
|
39
|
+
DATATYPE_URI = /^\^\^<([^>]+)>/.freeze
|
40
|
+
LITERAL = Regexp.union(LITERAL_WITH_LANGUAGE, LITERAL_WITH_DATATYPE, LITERAL_PLAIN).freeze
|
41
|
+
SUBJECT = Regexp.union(URIREF, NODEID).freeze
|
42
|
+
PREDICATE = Regexp.union(URIREF).freeze
|
43
|
+
OBJECT = Regexp.union(URIREF, NODEID, LITERAL).freeze
|
44
|
+
|
45
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
46
|
+
ESCAPE_CHARS = ["\t", "\n", "\r", "\"", "\\"].freeze
|
47
|
+
ESCAPE_CHAR4 = /\\u([0-9A-Fa-f]{4,4})/.freeze
|
48
|
+
ESCAPE_CHAR8 = /\\U([0-9A-Fa-f]{8,8})/.freeze
|
49
|
+
ESCAPE_CHAR = Regexp.union(ESCAPE_CHAR4, ESCAPE_CHAR8).freeze
|
50
|
+
ESCAPE_SURROGATE = /\\u([0-9A-Fa-f]{4,4})\\u([0-9A-Fa-f]{4,4})/.freeze
|
51
|
+
ESCAPE_SURROGATE1 = (0xD800..0xDBFF).freeze
|
52
|
+
ESCAPE_SURROGATE2 = (0xDC00..0xDFFF).freeze
|
53
|
+
|
54
|
+
##
|
55
|
+
# Reconstructs an RDF value from its serialized N-Triples
|
56
|
+
# representation.
|
57
|
+
#
|
58
|
+
# @param [String] input
|
59
|
+
# @return [RDF::Term]
|
60
|
+
def self.unserialize(input)
|
61
|
+
case input
|
62
|
+
when nil then nil
|
63
|
+
else self.new(input).read_value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# @param [String] input
|
69
|
+
# @return [RDF::Resource]
|
70
|
+
def self.parse_subject(input)
|
71
|
+
parse_uri(input) || parse_node(input)
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# @param [String] input
|
76
|
+
# @return [RDF::URI]
|
77
|
+
def self.parse_predicate(input)
|
78
|
+
parse_uri(input, :intern => true)
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# @param [String] input
|
83
|
+
# @return [RDF::Term]
|
84
|
+
def self.parse_object(input)
|
85
|
+
parse_uri(input) || parse_node(input) || parse_literal(input)
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# @param [String] input
|
90
|
+
# @return [RDF::Node]
|
91
|
+
def self.parse_node(input)
|
92
|
+
if input =~ NODEID
|
93
|
+
RDF::Node.new($1)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# @param [String] input
|
99
|
+
# @return [RDF::URI]
|
100
|
+
def self.parse_uri(input, options = {})
|
101
|
+
if input =~ URIREF
|
102
|
+
RDF::URI.send(options[:intern] ? :intern : :new, $1)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# @param [String] input
|
108
|
+
# @return [RDF::Literal]
|
109
|
+
def self.parse_literal(input)
|
110
|
+
case input
|
111
|
+
when LITERAL_WITH_LANGUAGE
|
112
|
+
RDF::Literal.new(unescape($1), :language => $2)
|
113
|
+
when LITERAL_WITH_DATATYPE
|
114
|
+
RDF::Literal.new(unescape($1), :datatype => $2)
|
115
|
+
when LITERAL_PLAIN
|
116
|
+
RDF::Literal.new(unescape($1))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# @param [String] string
|
122
|
+
# @return [String]
|
123
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
124
|
+
# @see http://blog.grayproductions.net/articles/understanding_m17n
|
125
|
+
# @see http://yehudakatz.com/2010/05/17/encodings-unabridged/
|
126
|
+
def self.unescape(string)
|
127
|
+
string.force_encoding(Encoding::ASCII_8BIT) if string.respond_to?(:force_encoding)
|
128
|
+
|
129
|
+
# Decode \t|\n|\r|\"|\\ character escapes:
|
130
|
+
ESCAPE_CHARS.each { |escape| string.gsub!(escape.inspect[1...-1], escape) }
|
131
|
+
|
132
|
+
# Decode \uXXXX\uXXXX surrogate pairs:
|
133
|
+
while
|
134
|
+
(string.sub!(ESCAPE_SURROGATE) do
|
135
|
+
if ESCAPE_SURROGATE1.include?($1.hex) && ESCAPE_SURROGATE2.include?($2.hex)
|
136
|
+
s = [$1, $2].pack('H*H*')
|
137
|
+
s = s.respond_to?(:force_encoding) ?
|
138
|
+
s.force_encoding(Encoding::UTF_16BE).encode!(Encoding::UTF_8) : # for Ruby 1.9+
|
139
|
+
Iconv.conv('UTF-8', 'UTF-16BE', s) # for Ruby 1.8.x
|
140
|
+
else
|
141
|
+
s = [$1.hex].pack('U*') << '\u' << $2
|
142
|
+
end
|
143
|
+
s.respond_to?(:force_encoding) ? s.force_encoding(Encoding::ASCII_8BIT) : s
|
144
|
+
end)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Decode \uXXXX and \UXXXXXXXX code points:
|
148
|
+
string.gsub!(ESCAPE_CHAR) do
|
149
|
+
s = [($1 || $2).hex].pack('U*')
|
150
|
+
s.respond_to?(:force_encoding) ? s.force_encoding(Encoding::ASCII_8BIT) : s
|
151
|
+
end
|
152
|
+
|
153
|
+
string.force_encoding(Encoding::UTF_8) if string.respond_to?(:force_encoding)
|
154
|
+
string
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# @return [RDF::Term]
|
159
|
+
def read_value
|
160
|
+
begin
|
161
|
+
read_statement
|
162
|
+
rescue RDF::ReaderError => e
|
163
|
+
read_uriref || read_node || read_literal
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
##
|
168
|
+
# @return [Array]
|
169
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar
|
170
|
+
def read_triple
|
171
|
+
loop do
|
172
|
+
readline.strip! # EOFError thrown on end of input
|
173
|
+
line = @line # for backtracking input in case of parse error
|
174
|
+
|
175
|
+
begin
|
176
|
+
unless blank? || read_comment
|
177
|
+
subject = read_uriref || read_node || fail_subject
|
178
|
+
predicate = read_uriref(:intern => true) || fail_predicate
|
179
|
+
object = read_uriref || read_node || read_literal || fail_object
|
180
|
+
return [subject, predicate, object]
|
181
|
+
end
|
182
|
+
rescue RDF::ReaderError => e
|
183
|
+
@line = line # this allows #read_value to work
|
184
|
+
raise e
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# @return [Boolean]
|
191
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (comment)
|
192
|
+
def read_comment
|
193
|
+
match(COMMENT)
|
194
|
+
end
|
195
|
+
|
196
|
+
##
|
197
|
+
# @return [RDF::URI]
|
198
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (uriref)
|
199
|
+
def read_uriref(options = {})
|
200
|
+
if uri_str = match(URIREF)
|
201
|
+
uri_str = self.class.unescape(uri_str)
|
202
|
+
uri = RDF::URI.send(intern? && options[:intern] ? :intern : :new, uri_str)
|
203
|
+
uri.validate! if validate?
|
204
|
+
uri.canonicalize! if canonicalize?
|
205
|
+
uri
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
##
|
210
|
+
# @return [RDF::Node]
|
211
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (nodeID)
|
212
|
+
def read_node
|
213
|
+
if node_id = match(NODEID)
|
214
|
+
@nodes ||= {}
|
215
|
+
@nodes[node_id] ||= RDF::Node.new(node_id)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
# @return [RDF::Literal]
|
221
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (literal)
|
222
|
+
def read_literal
|
223
|
+
if literal_str = match(LITERAL_PLAIN)
|
224
|
+
literal_str = self.class.unescape(literal_str)
|
225
|
+
literal = case
|
226
|
+
when language = match(LANGUAGE_TAG)
|
227
|
+
RDF::Literal.new(literal_str, :language => language)
|
228
|
+
when datatype = match(/^(\^\^)/) # FIXME
|
229
|
+
RDF::Literal.new(literal_str, :datatype => read_uriref || fail_object)
|
230
|
+
else
|
231
|
+
RDF::Literal.new(literal_str) # plain string literal
|
232
|
+
end
|
233
|
+
literal.validate! if validate?
|
234
|
+
literal.canonicalize! if canonicalize?
|
235
|
+
literal
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end # Reader
|
239
|
+
end # RDF::NTriples
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module RDF::NTriples
|
2
|
+
##
|
3
|
+
# N-Triples serializer.
|
4
|
+
#
|
5
|
+
# @example Obtaining an NTriples writer class
|
6
|
+
# RDF::Writer.for(:ntriples) #=> RDF::NTriples::Writer
|
7
|
+
# RDF::Writer.for("etc/test.nt")
|
8
|
+
# RDF::Writer.for(:file_name => "etc/test.nt")
|
9
|
+
# RDF::Writer.for(:file_extension => "nt")
|
10
|
+
# RDF::Writer.for(:content_type => "text/plain")
|
11
|
+
#
|
12
|
+
# @example Serializing RDF statements into an NTriples file
|
13
|
+
# RDF::NTriples::Writer.open("etc/test.nt") do |writer|
|
14
|
+
# graph.each_statement do |statement|
|
15
|
+
# writer << statement
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example Serializing RDF statements into an NTriples string
|
20
|
+
# RDF::NTriples::Writer.buffer do |writer|
|
21
|
+
# graph.each_statement do |statement|
|
22
|
+
# writer << statement
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntriples
|
27
|
+
class Writer < RDF::Writer
|
28
|
+
format RDF::NTriples::Format
|
29
|
+
|
30
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
31
|
+
ESCAPE_PLAIN = /\A[\x20-\x21\x23-\x5B\x5D-\x7E]*\z/m.freeze
|
32
|
+
ESCAPE_ASCII = /\A[\x00-\x7F]*\z/m.freeze
|
33
|
+
|
34
|
+
##
|
35
|
+
# @param [String] string
|
36
|
+
# @return [String]
|
37
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
38
|
+
def self.escape(string)
|
39
|
+
case
|
40
|
+
when string =~ ESCAPE_PLAIN # a shortcut for the simple case
|
41
|
+
string
|
42
|
+
when string.respond_to?(:ascii_only?) && string.ascii_only?
|
43
|
+
StringIO.open do |buffer|
|
44
|
+
string.each_byte { |u| buffer << escape_ascii(u) }
|
45
|
+
buffer.string
|
46
|
+
end
|
47
|
+
when string.respond_to?(:each_codepoint)
|
48
|
+
StringIO.open do |buffer|
|
49
|
+
string.each_codepoint { |u| buffer << escape_unicode(u) }
|
50
|
+
buffer.string
|
51
|
+
end
|
52
|
+
else # works in Ruby 1.8.x, too
|
53
|
+
StringIO.open do |buffer|
|
54
|
+
string.scan(/./mu) { |c| buffer << escape_unicode(u = c.unpack('U*').first) }
|
55
|
+
buffer.string
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# @param [Integer, #ord] u
|
62
|
+
# @return [String]
|
63
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
64
|
+
def self.escape_unicode(u)
|
65
|
+
case (u = u.ord)
|
66
|
+
when (0x00..0x7F) # ASCII 7-bit
|
67
|
+
escape_ascii(u)
|
68
|
+
when (0x80..0xFFFF) # Unicode BMP
|
69
|
+
escape_utf16(u)
|
70
|
+
when (0x10000..0x10FFFF) # Unicode
|
71
|
+
escape_utf32(u)
|
72
|
+
else
|
73
|
+
raise ArgumentError.new("expected a Unicode codepoint in (0x00..0x10FFFF), but got 0x#{u.to_s(16)}")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @param [Integer, #ord] u
|
79
|
+
# @return [String]
|
80
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
81
|
+
def self.escape_ascii(u)
|
82
|
+
case (u = u.ord)
|
83
|
+
when (0x00..0x08) then escape_utf16(u)
|
84
|
+
when (0x09) then "\\t"
|
85
|
+
when (0x0A) then "\\n"
|
86
|
+
when (0x0B..0x0C) then escape_utf16(u)
|
87
|
+
when (0x0D) then "\\r"
|
88
|
+
when (0x0E..0x1F) then escape_utf16(u)
|
89
|
+
when (0x22) then "\\\""
|
90
|
+
when (0x5C) then "\\\\"
|
91
|
+
when (0x7F) then escape_utf16(u)
|
92
|
+
when (0x00..0x7F) then u.chr
|
93
|
+
else
|
94
|
+
raise ArgumentError.new("expected an ASCII character in (0x00..0x7F), but got 0x#{u.to_s(16)}")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# @param [Integer, #ord] u
|
100
|
+
# @return [String]
|
101
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
102
|
+
def self.escape_utf16(u)
|
103
|
+
sprintf("\\u%04X", u.ord)
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# @param [Integer, #ord] u
|
108
|
+
# @return [String]
|
109
|
+
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
110
|
+
def self.escape_utf32(u)
|
111
|
+
sprintf("\\U%08X", u.ord)
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Returns the serialized N-Triples representation of the given RDF
|
116
|
+
# value.
|
117
|
+
#
|
118
|
+
# @param [RDF::Value] value
|
119
|
+
# @return [String]
|
120
|
+
# @raise [ArgumentError] if `value` is not an `RDF::Statement` or `RDF::Term`
|
121
|
+
def self.serialize(value)
|
122
|
+
writer = self.new
|
123
|
+
case value
|
124
|
+
when nil then nil
|
125
|
+
when FalseClass then value.to_s
|
126
|
+
when RDF::Statement
|
127
|
+
writer.format_statement(value) + "\n"
|
128
|
+
when RDF::Term
|
129
|
+
writer.format_term(value)
|
130
|
+
else
|
131
|
+
raise ArgumentError, "expected an RDF::Statement or RDF::Term, but got #{value.inspect}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# Outputs an N-Triples comment line.
|
137
|
+
#
|
138
|
+
# @param [String] text
|
139
|
+
# @return [void]
|
140
|
+
def write_comment(text)
|
141
|
+
puts "# #{text.chomp}" # TODO: correctly output multi-line comments
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# Outputs the N-Triples representation of a triple.
|
146
|
+
#
|
147
|
+
# @param [RDF::Resource] subject
|
148
|
+
# @param [RDF::URI] predicate
|
149
|
+
# @param [RDF::Term] object
|
150
|
+
# @return [void]
|
151
|
+
def write_triple(subject, predicate, object)
|
152
|
+
puts format_triple(subject, predicate, object)
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Returns the N-Triples representation of a statement.
|
157
|
+
#
|
158
|
+
# @param [RDF::Statement] statement
|
159
|
+
# @return [String]
|
160
|
+
def format_statement(statement)
|
161
|
+
format_triple(*statement.to_triple)
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Returns the N-Triples representation of a triple.
|
166
|
+
#
|
167
|
+
# @param [RDF::Resource] subject
|
168
|
+
# @param [RDF::URI] predicate
|
169
|
+
# @param [RDF::Term] object
|
170
|
+
# @return [String]
|
171
|
+
def format_triple(subject, predicate, object)
|
172
|
+
"%s %s %s ." % [subject, predicate, object].map { |value| format_term(value) }
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# Returns the N-Triples representation of a blank node.
|
177
|
+
#
|
178
|
+
# @param [RDF::Node] node
|
179
|
+
# @param [Hash{Symbol => Object}] options
|
180
|
+
# @return [String]
|
181
|
+
def format_node(node, options = {})
|
182
|
+
"_:%s" % node.id
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Returns the N-Triples representation of a URI reference.
|
187
|
+
#
|
188
|
+
# @param [RDF::URI] literal
|
189
|
+
# @param [Hash{Symbol => Object}] options
|
190
|
+
# @return [String]
|
191
|
+
def format_uri(uri, options = {})
|
192
|
+
"<%s>" % escaped(uri_for(uri))
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
196
|
+
# Returns the N-Triples representation of a literal.
|
197
|
+
#
|
198
|
+
# @param [RDF::Literal, String, #to_s] literal
|
199
|
+
# @param [Hash{Symbol => Object}] options
|
200
|
+
# @return [String]
|
201
|
+
def format_literal(literal, options = {})
|
202
|
+
case literal
|
203
|
+
when RDF::Literal
|
204
|
+
text = quoted(escaped(literal.value))
|
205
|
+
text << "@#{literal.language}" if literal.has_language?
|
206
|
+
text << "^^<#{uri_for(literal.datatype)}>" if literal.has_datatype?
|
207
|
+
text
|
208
|
+
else
|
209
|
+
quoted(escaped(literal.to_s))
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
# @private
|
215
|
+
def escaped(string)
|
216
|
+
self.class.escape(string)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|