1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
module OpenTox
module Backend
class FourStore
#TODO: catch 4store errors
@@mime_format = {
"application/rdf+xml" => :rdfxml,
"text/turtle" => :turtle,
"text/plain" => :ntriples,
"text/uri-list" => :uri_list,
#"application/json" => :json,
#"application/x-yaml" => :yaml,
#"text/x-yaml" => :yaml,
#"text/yaml" => :yaml,
"text/html" => :html,
# TODO: compression, forms
#/sparql/ => :sparql #removed to prevent sparql injections
}
@@format_mime = {
:rdfxml => "application/rdf+xml",
:turtle => "text/turtle",
:ntriples => "text/plain",
:uri_list => "text/uri-list",
#:json => "application/json",
#:yaml => "text/yaml",
:html => "text/html",
}
@@accept_formats = [:rdfxml, :turtle, :ntriples, :uri_list, :html] #, :json, :yaml]
@@content_type_formats = [:rdfxml, :turtle, :ntriples]#, :json, :yaml]
@@rdf_formats = [:rdfxml, :turtle, :ntriples]
def self.list mime_type
mime_type = "text/html" if mime_type.match(%r{\*/\*})
bad_request_error "'#{mime_type}' is not a supported mime type. Please specify one of #{@@accept_formats.collect{|f| @@format_mime[f]}.join(", ")} in the Accept Header." unless @@accept_formats.include? @@mime_format[mime_type]
if mime_type =~ /json|yaml|uri-list/
sparql = "SELECT ?s WHERE {?s <#{RDF.type}> <#{@@class}>. }"
elsif mime_type =~ /turtle|html|rdf|plain/
sparql = "CONSTRUCT {?s ?p ?o.} WHERE {?s <#{RDF.type}> <#{@@class}>; ?p ?o. }"
end
query sparql, mime_type
end
def self.get uri, mime_type
mime_type = "text/html" if mime_type.match(%r{\*/\*})
bad_request_error "'#{mime_type}' is not a supported mime type. Please specify one of #{@@accept_formats.collect{|f| @@format_mime[f]}.join(", ")} in the Accept Header." unless @@accept_formats.include? @@mime_format[mime_type]
not_found_error "#{uri} not found." unless list("text/uri-list").split("\n").include?(uri)
sparql = "CONSTRUCT {?s ?p ?o.} FROM <#{uri}> WHERE { ?s ?p ?o. }"
query sparql, mime_type
end
def self.post uri, rdf, mime_type
bad_request_error "'#{mime_type}' is not a supported content type. Please use one of #{@@content_type_formats.collect{|f| @@format_mime[f]}.join(", ")}." unless @@content_type_formats.include? @@mime_format[mime_type]
rdf = convert rdf, @@mime_format[mime_type], :ntriples#, uri unless mime_type == 'text/plain'
RestClient.post File.join(four_store_uri,"data")+"/", :data => rdf, :graph => uri, "mime-type" => "application/x-turtle" # not very consistent in 4store
end
def self.put uri, rdf, mime_type, skip_rewrite=false
bad_request_error "'#{mime_type}' is not a supported content type. Please use one of #{@@content_type_formats.collect{|f| @@format_mime[f]}.join(", ")}." unless @@content_type_formats.include? @@mime_format[mime_type]
uuid = uri.sub(/\/$/,'').split('/').last
bad_request_error "'#{uri}' is not a valid URI." unless uuid =~ /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/
if !skip_rewrite
rdf = convert rdf, @@mime_format[mime_type], :ntriples, uri
elsif mime_type != "text/plain" # ntriples are not converted
rdf = convert rdf, @@mime_format[mime_type], :ntriples
end
rdf = "<#{uri}> <#{RDF.type}> <#{@@class}>." unless rdf # create empty resource
RestClient.put File.join(four_store_uri,"data",uri), rdf, :content_type => "application/x-turtle" # content-type not very consistent in 4store
end
def self.delete uri
RestClientWrapper.delete data_uri uri
end
def self.query sparql, mime_type
if sparql =~ /SELECT/i
xml = RestClient.post File.join(four_store_uri,"sparql")+"/", :query => sparql
#TODO request tab delimited format to speed up parsing
list = parse_sparql_xml_results(xml).collect{|hash| hash["s"]}
case mime_type
when /json/
return list.to_json
when /yaml/
return list.to_yaml
when /uri-list/
return list.join "\n"
else
bad_request_error "#{mime_type} is not a supported mime type for SELECT statements. Please use one of text/uri-list, application/json, text/yaml, text/html."
end
elsif sparql =~ /CONSTRUCT/i
nt = RestClient.get(sparql_uri, :params => { :query => sparql }, :accept => "text/plain").body
return nt if mime_type == 'text/plain'
case mime_type
when /turtle/
return convert(nt,:ntriples, :turtle)
when /html/
# TODO: fix and improve
html = "<html><body>"
html += convert(nt,:ntriples, :turtle).gsub(%r{<(.*)>},'<<a href="\1">\1</a>>').gsub(/\n/,'<br/>')#.gsub(/ /,' ')
html += "</body></html>"
return html
when "application/rdf+xml"
return convert(nt,:ntriples, :rdfxml)
end
else
# TODO: check if this prevents SPARQL injections
bad_request_error "Only SELECT and CONSTRUCT are accepted SPARQL statements."
end
end
private
def self.convert rdf_string, input_format, output_format, rewrite_uri=nil
rewrite_uri ? serialize(parse_and_rewrite_uri(rdf_string,input_format, rewrite_uri), output_format) : serialize(parse(rdf_string,input_format), output_format)
end
def self.parse_and_rewrite_uri string, format, rewrite_uri
rdf = RDF::Graph.new
subject = nil
statements = [] # use array instead of graph for performance reasons
RDF::Reader.for(format).new(string) do |reader|
reader.each_statement do |statement|
subject = statement.subject if statement.predicate == RDF.type and statement.object == @@class
statements << statement
end
end
bad_request_error "No class specified with <#{RDF.type}> statement." unless subject
statements.each do |statement|
statement.subject = RDF::URI.new rewrite_uri if rewrite_uri and statement.subject == subject
rdf << statement
end
rdf
end
def self.parse string, format
rdf = RDF::Graph.new
RDF::Reader.for(format).new(string) do |reader|
reader.each_statement { |statement| rdf << statement }
end
rdf
end
def self.serialize rdf, format
if format == :turtle # prefixes seen to need RDF::N3
# TODO add prefixes
string = RDF::N3::Writer.for(format).buffer(:prefixes => {:ot => "http://www.opentox.org/api/1.2#"}) do |writer|
rdf.each{|statement| writer << statement}
end
else
string = RDF::Writer.for(format).buffer do |writer|
rdf.each{|statement| writer << statement}
end
end
string
end
def self.four_store_uri
$four_store[:uri].sub(%r{//},"//#{$four_store[:user]}:#{$four_store[:password]}@")
end
def self.sparql_uri
File.join(four_store_uri, "sparql") + '/'
end
def self.data_uri uri
File.join(four_store_uri, "data","?graph=#{uri}")
end
def self.parse_sparql_xml_results(xml)
results = []
doc = REXML::Document.new(REXML::Source.new(xml))
doc.elements.each("*/results/result") do |result|
result_hash = {}
result.elements.each do |binding|
key = binding.attributes["name"]
value = binding.elements[1].text
type = binding.elements[1].name
result_hash[key] = value
end
results.push result_hash
end
results
end
end
end
end
|