rubyrdf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/History.txt +4 -0
  2. data/License.txt +24 -0
  3. data/Manifest.txt +52 -0
  4. data/README.txt +79 -0
  5. data/Rakefile +4 -0
  6. data/config/hoe.rb +70 -0
  7. data/config/requirements.rb +15 -0
  8. data/lib/rdf/blank_node.rb +41 -0
  9. data/lib/rdf/exceptions.rb +26 -0
  10. data/lib/rdf/format/ntriples.rb +493 -0
  11. data/lib/rdf/graph/base.rb +118 -0
  12. data/lib/rdf/graph/memory.rb +146 -0
  13. data/lib/rdf/graph/tests.rb +137 -0
  14. data/lib/rdf/namespace.rb +90 -0
  15. data/lib/rdf/plain_literal_node.rb +36 -0
  16. data/lib/rdf/query/binding.rb +68 -0
  17. data/lib/rdf/query/executer.rb +42 -0
  18. data/lib/rdf/query/result.rb +54 -0
  19. data/lib/rdf/query.rb +54 -0
  20. data/lib/rdf/triple.rb +61 -0
  21. data/lib/rdf/typed_literal_node.rb +39 -0
  22. data/lib/rdf/uri_node.rb +35 -0
  23. data/lib/rdf/version.rb +9 -0
  24. data/lib/rubyrdf.rb +59 -0
  25. data/log/debug.log +0 -0
  26. data/script/console +11 -0
  27. data/script/destroy +14 -0
  28. data/script/generate +14 -0
  29. data/script/txt2html +74 -0
  30. data/setup.rb +1585 -0
  31. data/tasks/deployment.rake +34 -0
  32. data/tasks/environment.rake +7 -0
  33. data/tasks/website.rake +17 -0
  34. data/test/helper.rb +14 -0
  35. data/test/test_blank_node.rb +67 -0
  36. data/test/test_format_ntriples.rb +247 -0
  37. data/test/test_graph_base.rb +71 -0
  38. data/test/test_graph_memory.rb +146 -0
  39. data/test/test_namespace.rb +130 -0
  40. data/test/test_plain_literal_node.rb +83 -0
  41. data/test/test_query.rb +49 -0
  42. data/test/test_query_binding.rb +84 -0
  43. data/test/test_query_result.rb +111 -0
  44. data/test/test_rdf.rb +56 -0
  45. data/test/test_triple.rb +147 -0
  46. data/test/test_typed_literal_node.rb +61 -0
  47. data/test/test_uri_node.rb +45 -0
  48. data/website/index.html +169 -0
  49. data/website/index.txt +92 -0
  50. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  51. data/website/stylesheets/screen.css +138 -0
  52. data/website/template.html.erb +48 -0
  53. metadata +139 -0
@@ -0,0 +1,493 @@
1
+ require 'stringio'
2
+
3
+ module RDF
4
+ module Format
5
+ # Import/Exports a graph to NTriples format.
6
+ class NTriples
7
+ # Raised when there is a syntax error in an input NTriples file.
8
+ class SyntaxError < RDF::Error; end
9
+
10
+ # Exports a +graph+ to +file+ in NTriples format.
11
+ def self.export(graph, file)
12
+ graph.each do |t|
13
+ file.write(export_node(t.subject))
14
+ file.write(' ')
15
+ file.write(export_node(t.predicate))
16
+ file.write(' ')
17
+ file.write(export_node(t.object))
18
+ file.puts('.')
19
+ end
20
+ end
21
+
22
+ # Returns a string NTriples representation of +node+.
23
+ def self.export_node(node)
24
+ case
25
+ when RDF::UriNode?(node)
26
+ "<#{node.uri}>"
27
+ when RDF::BlankNode?(node)
28
+ "_:#{node.name}"
29
+ when RDF::PlainLiteralNode?(node)
30
+ "\"#{node.lexical_form}\"" + (node.language_tag.to_s.empty? ? '' : "@#{node.language_tag}")
31
+ when RDF::TypedLiteralNode?(node)
32
+ "\"#{node.lexical_form}\"^^<#{node.datatype_uri}>"
33
+ else
34
+ raise SyntaxError, "Unknown node type"
35
+ end
36
+ end
37
+
38
+ # Imports NTriples input +file+ to +graph+ or creates a new graph if +graph+ is nil.
39
+ def self.import(file, graph = nil)
40
+ NTriples.new(file, graph).import
41
+ end
42
+
43
+ # Imports a string NTriples representation of a node into +graph+ or creates a new graph if +graph+ is nil.
44
+ def self.import_node(str, graph = nil)
45
+ NTriples.new(StringIO.new(str), graph).import_node
46
+ end
47
+
48
+ # Creates an instance of NTriples for importing an NTriples +file+ into +graph+.
49
+ def initialize(file, graph = nil)
50
+ @lineno = 1
51
+ @charno = 1
52
+ @file = file.respond_to?(:getc) ? file : StringIO.new(file.to_s)
53
+ stretch_buf_to(1)
54
+ @graph = graph.nil? ? RDF::Graph::Memory.new : graph
55
+ end
56
+
57
+ # Runs the import for this NTriples instance, and returns the graph.
58
+ def import
59
+ lines unless @file.eof?
60
+ @graph
61
+ end
62
+
63
+ # Runs the import for a single node for this NTriples instance, and returns the node.
64
+ def import_node
65
+ if ws?
66
+ wses
67
+ end
68
+
69
+ object
70
+ end
71
+
72
+ private
73
+ def position
74
+ "line: #{@lineno}, char: #{@charno}"
75
+ end
76
+
77
+ # All of the internal parsing methods follow
78
+ def stretch_buf_to(x)
79
+ @buf ||= ''
80
+ goal = x - @buf.length
81
+ while goal > 0 && !@file.eof?
82
+ @buf << @file.getc.chr
83
+ goal -= 1
84
+ @charno += 1
85
+ end
86
+ end
87
+
88
+ def consume(str = nil)
89
+ if str.nil?
90
+ stretch_buf_to(2)
91
+ @buf.slice!(0).chr
92
+ elsif test(str)
93
+ stretch_buf_to(str.length + 1)
94
+
95
+ if @buf.length >= str.length
96
+ @buf.slice!(0, str.length)
97
+ end
98
+ else
99
+ raise SyntaxError, "Expected #{str} at #{position}"
100
+ end
101
+ end
102
+
103
+ def test(str)
104
+ stretch_buf_to(str.length)
105
+ @buf[0, str.length] == str
106
+ end
107
+
108
+ def peek
109
+ @buf[0, 1]
110
+ end
111
+
112
+ def unescape(str)
113
+ str
114
+ end
115
+
116
+ ## Parsing Methods
117
+ def lines
118
+ line
119
+ while line?
120
+ line
121
+ end
122
+ end
123
+
124
+ def line
125
+ @charno = 1
126
+ if ws?
127
+ wses
128
+ end
129
+
130
+ case
131
+ when comment?
132
+ comment
133
+ eoln
134
+ when triple?
135
+ triple
136
+ eoln
137
+ when eoln? || @file.eof?
138
+ eoln
139
+ else
140
+ raise SyntaxError, "Expected comment, triple, or empty line at #{position}"
141
+ end
142
+ @lineno += 1
143
+ end
144
+
145
+ def wses
146
+ str = ws
147
+ while ws?
148
+ str << ws
149
+ end
150
+ str
151
+ end
152
+
153
+ def ws
154
+ if space? || tab?
155
+ consume
156
+ else
157
+ raise SyntaxError, "Expected space or tab at #{position}"
158
+ end
159
+ end
160
+
161
+ def comment
162
+ str = consume('#')
163
+ while character_no_cr_or_lf?
164
+ str = consume
165
+ end
166
+ str
167
+ end
168
+
169
+ def triple
170
+ sub = subject
171
+ wses
172
+ pred = predicate
173
+ wses
174
+ obj = object
175
+ if ws?
176
+ wses
177
+ end
178
+ consume('.')
179
+ if ws?
180
+ wses
181
+ end
182
+ @graph.add(RDF::Triple.new(sub, pred, obj))
183
+ end
184
+
185
+ def subject
186
+ if uriRef?
187
+ uriRef
188
+ elsif nodeId?
189
+ nodeId
190
+ else
191
+ raise SyntaxError, "Expected uriRef or nodeId at #{position}"
192
+ end
193
+ end
194
+
195
+ def predicate
196
+ uriRef
197
+ end
198
+
199
+ def object
200
+ if uriRef?
201
+ uriRef
202
+ elsif nodeId?
203
+ nodeId
204
+ elsif lit_string?
205
+ literal
206
+ end
207
+ end
208
+
209
+ def uriRef
210
+ consume('<')
211
+ node = RDF::UriNode.new(absoluteUri)
212
+ consume('>')
213
+ node
214
+ end
215
+
216
+ def absoluteUri
217
+ str = character
218
+ while character? && !test('>')
219
+ str << character
220
+ end
221
+ unescape(str)
222
+ end
223
+
224
+ def nodeId
225
+ consume('_:')
226
+ RDF::BlankNode.new(name, @graph)
227
+ end
228
+
229
+ def name
230
+ str = ''
231
+ if cap_az? || az?
232
+ str << consume
233
+ else
234
+ raise SyntaxError, "Expected A-Z or a-z at #{position}"
235
+ end
236
+
237
+ while cap_az? || az? || num?
238
+ str << consume
239
+ end
240
+ str
241
+ end
242
+
243
+ def literal
244
+ str = lit_string
245
+ if lang?
246
+ lit_lang = lang
247
+ elsif datatype?
248
+ lit_dt = datatype
249
+ end
250
+
251
+ if lit_dt
252
+ RDF::TypedLiteralNode.new(str, lit_dt)
253
+ else
254
+ RDF::PlainLiteralNode.new(str, lit_lang)
255
+ end
256
+ end
257
+
258
+ def lit_string
259
+ consume('"')
260
+ str = string
261
+ consume('"')
262
+ unescape(str)
263
+ end
264
+
265
+ def string
266
+ str = ''
267
+ backslash = false
268
+ while character? && (!test('"') || backslash)
269
+ char = character
270
+ if char == '\\' && !backslash
271
+ backslash = true
272
+ else
273
+ if backslash
274
+ backslash = false
275
+ char = convert_backslash(char)
276
+ end
277
+
278
+ str << char
279
+ end
280
+ end
281
+ str
282
+ end
283
+
284
+ def convert_backslash(char)
285
+ case char
286
+ when 'n'
287
+ "\n"
288
+ when 'r'
289
+ "\r"
290
+ when 't'
291
+ "\t"
292
+ when 'u'
293
+ small_unicode_value
294
+ when 'U'
295
+ long_unicode_value
296
+ else
297
+ char
298
+ end
299
+ end
300
+
301
+ def small_unicode_value
302
+ value = ''
303
+ while peek =~ /[A-Fa-f0-9]/
304
+ value << consume
305
+ end
306
+
307
+ if value.size <= 4
308
+ value.hex.utf8
309
+ else
310
+ raise SyntaxError, 'Expected no more than four hexadecimal characters'
311
+ end
312
+ end
313
+
314
+ def long_unicode_value
315
+ value = ''
316
+ while peek =~ /[A-Fa-f0-9]/
317
+ value << consume
318
+ end
319
+
320
+ if value.size >= 5 && value.size <= 8
321
+ value.hex.utf8
322
+ else
323
+ raise SyntaxError, 'Expected between five and eight hexadecimal characters'
324
+ end
325
+ end
326
+
327
+ def lang
328
+ consume('@')
329
+ language
330
+ end
331
+
332
+ def language
333
+ str = ''
334
+ if az?
335
+ str << consume
336
+ else
337
+ raise SyntaxError, "Expected a-z at #{position}"
338
+ end
339
+
340
+ while az? && !test('-')
341
+ str << consume
342
+ end
343
+
344
+ while test('-')
345
+ str << consume
346
+ if az? || num?
347
+ str << consume
348
+ else
349
+ raise SyntaxError, "Expected a-z or number at #{position}"
350
+ end
351
+
352
+ while az? || num?
353
+ str << consume
354
+ end
355
+ end
356
+ str
357
+ end
358
+
359
+ def datatype
360
+ consume('^^')
361
+ uriRef.uri
362
+ end
363
+
364
+ def character
365
+ if character?
366
+ consume
367
+ else
368
+ raise SyntaxError, "Expected character at #{position}"
369
+ end
370
+ end
371
+
372
+ def eoln
373
+ str = ''
374
+ if cr?
375
+ str = consume
376
+ if lf?
377
+ str << consume
378
+ end
379
+ elsif lf?
380
+ str = consume
381
+ elsif !@file.eof?
382
+ raise SyntaxError, "Expected cr, lf, or crlf at #{position}"
383
+ end
384
+ str
385
+ end
386
+
387
+ ## Test Methods
388
+ def line?
389
+ ws? || comment? || uriRef? || nodeId? || eoln?
390
+ end
391
+
392
+ def comment?
393
+ test('#')
394
+ end
395
+
396
+ def triple?
397
+ subject?
398
+ end
399
+
400
+ def subject?
401
+ uriRef? || nodeId?
402
+ end
403
+
404
+ def predicate?
405
+ uriRef?
406
+ end
407
+
408
+ def object?
409
+ uriRef? || nodeId? || lit_string?
410
+ end
411
+
412
+ def uriRef?
413
+ test('<')
414
+ end
415
+
416
+ def nodeId?
417
+ test("_:")
418
+ end
419
+
420
+ def lit_string?
421
+ test('"')
422
+ end
423
+
424
+ def lang?
425
+ test("@")
426
+ end
427
+
428
+ def datatype?
429
+ test("^^")
430
+ end
431
+
432
+ def language_tag?
433
+ ('a'..'z').include?(peek)
434
+ end
435
+
436
+ def ws?
437
+ space? || tab?
438
+ end
439
+
440
+ def eoln?
441
+ cr? || lf?
442
+ end
443
+
444
+ def space?
445
+ peek == 0x20.chr
446
+ end
447
+
448
+ def cr?
449
+ peek == 0xD.chr
450
+ end
451
+
452
+ def lf?
453
+ peek == 0xA.chr
454
+ end
455
+
456
+ def tab?
457
+ peek == 0x9.chr
458
+ end
459
+
460
+ def string?
461
+ @file.eof? || character?
462
+ end
463
+
464
+ def cap_az?
465
+ ('A'..'Z').include?(peek)
466
+ end
467
+
468
+ def az?
469
+ ('a'..'z').include?(peek)
470
+ end
471
+
472
+ def num?
473
+ ('0'..'9').include?(peek)
474
+ end
475
+
476
+ def name?
477
+ cap_az? || az?
478
+ end
479
+
480
+ def absoluteUri?
481
+ character?
482
+ end
483
+
484
+ def character_no_cr_or_lf?
485
+ character? && !(cr? || lf?)
486
+ end
487
+
488
+ def character?
489
+ (0x20.chr .. 0x7E.chr).include?(peek)
490
+ end
491
+ end
492
+ end
493
+ end
@@ -0,0 +1,118 @@
1
+ module RDF
2
+ module Graph
3
+ # Base class for graph implementations. This class provides basic
4
+ # functionality and common interface for all graphs.
5
+ class Base
6
+ # Exports the triples for this graph in +format+ to +file+. If +file+ is
7
+ # nil, then the export is returned as a string.
8
+ #
9
+ # There is one format provided by default (:ntriples), but the supported
10
+ # formats are depended upon the particular graph implementation, and graph
11
+ # implementations can override this method to use a (presumably) faster
12
+ # export function from the underlying triple store--as long as it
13
+ # preserves the interface expectations.
14
+ #
15
+ # If a graph implementation overrides this method, and the underlying
16
+ # triple store does not support one of the default formats
17
+ # (see RDF::Format), then it is recommended that the implementation make
18
+ # use of the RDF::Format classes to provide exports for the default
19
+ # formats.
20
+ #
21
+ # *Raises*:: <tt></tt>
22
+ # UnsupportedFormatError:: if +format+ is not supported for export
23
+ def export(format = :ntriples, file = nil)
24
+ file ||= StringIO.new
25
+
26
+ case format
27
+ when :ntriples
28
+ RDF::Format::NTriples.export(self, file)
29
+ else
30
+ raise UnsupportedFormatError
31
+ end
32
+
33
+ file.string if file.is_a?(StringIO)
34
+ end
35
+
36
+ # Imports the triples from +file+ in +format+ to this graph.
37
+ #
38
+ # This method will not merge the graphs, meaning the blank nodes from
39
+ # +file+ are not renamed as they are imported. If you want to merge two
40
+ # graphs, then use merge.
41
+ #
42
+ # There is one format provided by default (:ntriples), but the supported
43
+ # formats are depended upon the particular graph implementation, and graph
44
+ # implementations can override this method to use a (presumably) faster
45
+ # import function from the underlying triple store--as long as it
46
+ # preserves the interface expectations.
47
+ #
48
+ # If a graph implementation overrides this method, and the underlying
49
+ # triple store does not support one of the default formats
50
+ # (see RDF::Format), then it is recommended that the implementation make
51
+ # use of the RDF::Format classes to provide imports for the default
52
+ # formats.
53
+ #
54
+ # *Raises*:: <tt></tt>
55
+ # ArgumentError:: if +file+ is nil
56
+ # UnsupportedFormatError:: if +format+ is not supported for import
57
+ def import(file, format = :ntriples)
58
+ raise ArgumentError, "file cannot be blank" if file.nil?
59
+
60
+ case format
61
+ when :ntriples
62
+ RDF::Format::NTriples.import(file, self)
63
+ else
64
+ raise UnsupportedFormatError
65
+ end
66
+ end
67
+
68
+ # Merges +graph+ into the current graph. In doing so it will rename all
69
+ # of the blank nodes so they don't collide with any existing blank nodes.
70
+ def merge(graph)
71
+ map = {}
72
+ graph.each do |t|
73
+ s, p, o = t.subject, t.predicate, t.object
74
+ if RDF::BlankNode?(s)
75
+ map[s] = new_blank_node(s.name) unless map.key?(s)
76
+ s = map[s]
77
+ end
78
+ if RDF::BlankNode?(o)
79
+ map[o] = new_blank_node(o.name) unless map.key?(o)
80
+ o = map[o]
81
+ end
82
+
83
+ add(s, p, o)
84
+ end
85
+ end
86
+
87
+ # Returns true if the triple (+s+, +p+, +o+) is in this graph, false
88
+ # otherwise.
89
+ def include?(*triple)
90
+ triple = Triple.construct(*triple)
91
+ return false if variable?(triple.subject) || variable?(triple.object)
92
+ execute(RDF::Query.new.where(triple.subject, triple.predicate, triple.object)).success?
93
+ end
94
+
95
+ def each
96
+ result = execute(RDF::Query.new.where(:s, :p, :o))
97
+ result.bindings.each do |b|
98
+ yield Triple.new(b[:s], b[:p], b[:o])
99
+ end
100
+ end
101
+
102
+ # Returns true if this graph is empty, false otherwise.
103
+ def empty?
104
+ size == 0
105
+ end
106
+
107
+ # Creates a new BlankNode associated with this graph.
108
+ def new_blank_node(name)
109
+ RDF::BlankNode.new(name, self)
110
+ end
111
+
112
+ private
113
+ def variable?(x)
114
+ RDF::BlankNode?(x) && x.graph != self
115
+ end
116
+ end
117
+ end
118
+ end