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.
Files changed (80) hide show
  1. data/AUTHORS +3 -0
  2. data/CREDITS +9 -0
  3. data/README +361 -0
  4. data/UNLICENSE +24 -0
  5. data/VERSION +1 -0
  6. data/bin/rdf +18 -0
  7. data/etc/doap.nt +62 -0
  8. data/lib/df.rb +1 -0
  9. data/lib/rdf/cli.rb +200 -0
  10. data/lib/rdf/format.rb +383 -0
  11. data/lib/rdf/mixin/countable.rb +39 -0
  12. data/lib/rdf/mixin/durable.rb +31 -0
  13. data/lib/rdf/mixin/enumerable.rb +637 -0
  14. data/lib/rdf/mixin/indexable.rb +26 -0
  15. data/lib/rdf/mixin/inferable.rb +5 -0
  16. data/lib/rdf/mixin/mutable.rb +191 -0
  17. data/lib/rdf/mixin/queryable.rb +265 -0
  18. data/lib/rdf/mixin/readable.rb +15 -0
  19. data/lib/rdf/mixin/type_check.rb +21 -0
  20. data/lib/rdf/mixin/writable.rb +152 -0
  21. data/lib/rdf/model/graph.rb +263 -0
  22. data/lib/rdf/model/list.rb +731 -0
  23. data/lib/rdf/model/literal/boolean.rb +121 -0
  24. data/lib/rdf/model/literal/date.rb +73 -0
  25. data/lib/rdf/model/literal/datetime.rb +72 -0
  26. data/lib/rdf/model/literal/decimal.rb +86 -0
  27. data/lib/rdf/model/literal/double.rb +189 -0
  28. data/lib/rdf/model/literal/integer.rb +126 -0
  29. data/lib/rdf/model/literal/numeric.rb +184 -0
  30. data/lib/rdf/model/literal/time.rb +87 -0
  31. data/lib/rdf/model/literal/token.rb +47 -0
  32. data/lib/rdf/model/literal/xml.rb +39 -0
  33. data/lib/rdf/model/literal.rb +373 -0
  34. data/lib/rdf/model/node.rb +156 -0
  35. data/lib/rdf/model/resource.rb +28 -0
  36. data/lib/rdf/model/statement.rb +296 -0
  37. data/lib/rdf/model/term.rb +77 -0
  38. data/lib/rdf/model/uri.rb +570 -0
  39. data/lib/rdf/model/value.rb +133 -0
  40. data/lib/rdf/nquads.rb +152 -0
  41. data/lib/rdf/ntriples/format.rb +48 -0
  42. data/lib/rdf/ntriples/reader.rb +239 -0
  43. data/lib/rdf/ntriples/writer.rb +219 -0
  44. data/lib/rdf/ntriples.rb +104 -0
  45. data/lib/rdf/query/pattern.rb +329 -0
  46. data/lib/rdf/query/solution.rb +252 -0
  47. data/lib/rdf/query/solutions.rb +237 -0
  48. data/lib/rdf/query/variable.rb +221 -0
  49. data/lib/rdf/query.rb +404 -0
  50. data/lib/rdf/reader.rb +511 -0
  51. data/lib/rdf/repository.rb +389 -0
  52. data/lib/rdf/transaction.rb +161 -0
  53. data/lib/rdf/util/aliasing.rb +63 -0
  54. data/lib/rdf/util/cache.rb +139 -0
  55. data/lib/rdf/util/file.rb +38 -0
  56. data/lib/rdf/util/uuid.rb +36 -0
  57. data/lib/rdf/util.rb +6 -0
  58. data/lib/rdf/version.rb +19 -0
  59. data/lib/rdf/vocab/cc.rb +18 -0
  60. data/lib/rdf/vocab/cert.rb +13 -0
  61. data/lib/rdf/vocab/dc.rb +63 -0
  62. data/lib/rdf/vocab/dc11.rb +23 -0
  63. data/lib/rdf/vocab/doap.rb +45 -0
  64. data/lib/rdf/vocab/exif.rb +168 -0
  65. data/lib/rdf/vocab/foaf.rb +69 -0
  66. data/lib/rdf/vocab/geo.rb +13 -0
  67. data/lib/rdf/vocab/http.rb +26 -0
  68. data/lib/rdf/vocab/owl.rb +59 -0
  69. data/lib/rdf/vocab/rdfs.rb +17 -0
  70. data/lib/rdf/vocab/rsa.rb +12 -0
  71. data/lib/rdf/vocab/rss.rb +14 -0
  72. data/lib/rdf/vocab/sioc.rb +93 -0
  73. data/lib/rdf/vocab/skos.rb +36 -0
  74. data/lib/rdf/vocab/wot.rb +21 -0
  75. data/lib/rdf/vocab/xhtml.rb +9 -0
  76. data/lib/rdf/vocab/xsd.rb +58 -0
  77. data/lib/rdf/vocab.rb +215 -0
  78. data/lib/rdf/writer.rb +475 -0
  79. data/lib/rdf.rb +192 -0
  80. 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