Chris@439
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@439
|
2
|
Chris@439
|
3 /*
|
Chris@439
|
4 Sonic Visualiser
|
Chris@439
|
5 An audio file viewer and annotation editor.
|
Chris@439
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@439
|
7 This file copyright 2008 QMUL.
|
Chris@439
|
8
|
Chris@439
|
9 This program is free software; you can redistribute it and/or
|
Chris@439
|
10 modify it under the terms of the GNU General Public License as
|
Chris@439
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@439
|
12 License, or (at your option) any later version. See the file
|
Chris@439
|
13 COPYING included with this distribution for more information.
|
Chris@439
|
14 */
|
Chris@439
|
15
|
Chris@439
|
16 #include "RDFTransformFactory.h"
|
Chris@439
|
17
|
Chris@439
|
18 #include <map>
|
Chris@439
|
19 #include <vector>
|
Chris@439
|
20
|
Chris@439
|
21 #include <iostream>
|
Chris@439
|
22 #include <cmath>
|
Chris@439
|
23
|
Chris@439
|
24 #include "SimpleSPARQLQuery.h"
|
Chris@439
|
25 #include "PluginRDFIndexer.h"
|
Chris@439
|
26 #include "base/ProgressReporter.h"
|
Chris@439
|
27
|
Chris@439
|
28 #include "transform/TransformFactory.h"
|
Chris@439
|
29
|
Chris@439
|
30 using std::cerr;
|
Chris@439
|
31 using std::endl;
|
Chris@439
|
32
|
Chris@439
|
33 typedef const unsigned char *STR; // redland's expected string type
|
Chris@439
|
34
|
Chris@439
|
35
|
Chris@439
|
36 class RDFTransformFactoryImpl
|
Chris@439
|
37 {
|
Chris@439
|
38 public:
|
Chris@439
|
39 RDFTransformFactoryImpl(QString url);
|
Chris@439
|
40 virtual ~RDFTransformFactoryImpl();
|
Chris@439
|
41
|
Chris@493
|
42 bool isRDF();
|
Chris@439
|
43 bool isOK();
|
Chris@439
|
44 QString getErrorString() const;
|
Chris@439
|
45
|
Chris@439
|
46 std::vector<Transform> getTransforms(ProgressReporter *);
|
Chris@439
|
47
|
Chris@439
|
48 protected:
|
Chris@439
|
49 QString m_urlString;
|
Chris@439
|
50 QString m_errorString;
|
Chris@493
|
51 bool m_isRDF;
|
Chris@489
|
52 bool setOutput(Transform &, QString);
|
Chris@489
|
53 bool setParameters(Transform &, QString);
|
Chris@439
|
54 };
|
Chris@439
|
55
|
Chris@439
|
56
|
Chris@439
|
57 QString
|
Chris@439
|
58 RDFTransformFactory::getKnownExtensions()
|
Chris@439
|
59 {
|
Chris@439
|
60 return "*.rdf *.n3 *.ttl";
|
Chris@439
|
61 }
|
Chris@439
|
62
|
Chris@439
|
63 RDFTransformFactory::RDFTransformFactory(QString url) :
|
Chris@439
|
64 m_d(new RDFTransformFactoryImpl(url))
|
Chris@439
|
65 {
|
Chris@439
|
66 }
|
Chris@439
|
67
|
Chris@439
|
68 RDFTransformFactory::~RDFTransformFactory()
|
Chris@439
|
69 {
|
Chris@439
|
70 delete m_d;
|
Chris@439
|
71 }
|
Chris@439
|
72
|
Chris@439
|
73 bool
|
Chris@493
|
74 RDFTransformFactory::isRDF()
|
Chris@493
|
75 {
|
Chris@493
|
76 return m_d->isRDF();
|
Chris@493
|
77 }
|
Chris@493
|
78
|
Chris@493
|
79 bool
|
Chris@439
|
80 RDFTransformFactory::isOK()
|
Chris@439
|
81 {
|
Chris@439
|
82 return m_d->isOK();
|
Chris@439
|
83 }
|
Chris@439
|
84
|
Chris@439
|
85 QString
|
Chris@439
|
86 RDFTransformFactory::getErrorString() const
|
Chris@439
|
87 {
|
Chris@439
|
88 return m_d->getErrorString();
|
Chris@439
|
89 }
|
Chris@439
|
90
|
Chris@439
|
91 std::vector<Transform>
|
Chris@439
|
92 RDFTransformFactory::getTransforms(ProgressReporter *r)
|
Chris@439
|
93 {
|
Chris@439
|
94 return m_d->getTransforms(r);
|
Chris@439
|
95 }
|
Chris@439
|
96
|
Chris@439
|
97 RDFTransformFactoryImpl::RDFTransformFactoryImpl(QString url) :
|
Chris@493
|
98 m_urlString(url),
|
Chris@493
|
99 m_isRDF(false)
|
Chris@439
|
100 {
|
Chris@439
|
101 }
|
Chris@439
|
102
|
Chris@439
|
103 RDFTransformFactoryImpl::~RDFTransformFactoryImpl()
|
Chris@439
|
104 {
|
Chris@492
|
105 SimpleSPARQLQuery::closeSingleSource(m_urlString);
|
Chris@439
|
106 }
|
Chris@439
|
107
|
Chris@439
|
108 bool
|
Chris@493
|
109 RDFTransformFactoryImpl::isRDF()
|
Chris@493
|
110 {
|
Chris@493
|
111 return m_isRDF;
|
Chris@493
|
112 }
|
Chris@493
|
113
|
Chris@493
|
114 bool
|
Chris@439
|
115 RDFTransformFactoryImpl::isOK()
|
Chris@439
|
116 {
|
Chris@439
|
117 return (m_errorString == "");
|
Chris@439
|
118 }
|
Chris@439
|
119
|
Chris@439
|
120 QString
|
Chris@439
|
121 RDFTransformFactoryImpl::getErrorString() const
|
Chris@439
|
122 {
|
Chris@439
|
123 return m_errorString;
|
Chris@439
|
124 }
|
Chris@439
|
125
|
Chris@439
|
126 std::vector<Transform>
|
Chris@439
|
127 RDFTransformFactoryImpl::getTransforms(ProgressReporter *reporter)
|
Chris@439
|
128 {
|
Chris@439
|
129 std::vector<Transform> transforms;
|
Chris@439
|
130
|
Chris@440
|
131 std::map<QString, Transform> uriTransformMap;
|
Chris@439
|
132
|
Chris@489
|
133 QString query =
|
Chris@440
|
134 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
|
Chris@439
|
135
|
Chris@489
|
136 " SELECT ?transform ?plugin "
|
Chris@440
|
137
|
Chris@440
|
138 " FROM <%2> "
|
Chris@439
|
139
|
Chris@440
|
140 " WHERE { "
|
Chris@440
|
141 " ?transform a vamp:Transform ; "
|
Chris@440
|
142 " vamp:plugin ?plugin . "
|
Chris@440
|
143 " } ";
|
Chris@440
|
144
|
Chris@440
|
145 SimpleSPARQLQuery transformsQuery
|
Chris@489
|
146 (SimpleSPARQLQuery::QueryFromSingleSource, query.arg(m_urlString));
|
Chris@440
|
147
|
Chris@440
|
148 SimpleSPARQLQuery::ResultList transformResults = transformsQuery.execute();
|
Chris@440
|
149
|
Chris@440
|
150 if (!transformsQuery.isOK()) {
|
Chris@440
|
151 m_errorString = transformsQuery.getErrorString();
|
Chris@439
|
152 return transforms;
|
Chris@439
|
153 }
|
Chris@439
|
154
|
Chris@493
|
155 m_isRDF = true;
|
Chris@493
|
156
|
Chris@440
|
157 if (transformResults.empty()) {
|
Chris@440
|
158 cerr << "RDFTransformFactory: NOTE: No RDF/TTL transform descriptions found in document at <" << m_urlString.toStdString() << ">" << endl;
|
Chris@439
|
159 return transforms;
|
Chris@439
|
160 }
|
Chris@439
|
161
|
Chris@489
|
162 // There are various queries we need to make that might include
|
Chris@489
|
163 // data from iether the transform RDF or the model accumulated
|
Chris@489
|
164 // from plugin descriptions. For example, the transform RDF may
|
Chris@489
|
165 // specify the output's true URI, or it might have a blank node or
|
Chris@489
|
166 // some other URI with the appropriate vamp:identifier included in
|
Chris@489
|
167 // the file. To cover both cases, we need to add the file itself
|
Chris@489
|
168 // into the model and always query the model using the transform
|
Chris@489
|
169 // URI rather than querying the file itself subsequently.
|
Chris@489
|
170
|
Chris@489
|
171 SimpleSPARQLQuery::addSourceToModel(m_urlString);
|
Chris@489
|
172
|
Chris@439
|
173 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
|
Chris@439
|
174
|
Chris@440
|
175 for (int i = 0; i < transformResults.size(); ++i) {
|
Chris@439
|
176
|
Chris@440
|
177 SimpleSPARQLQuery::KeyValueMap &result = transformResults[i];
|
Chris@439
|
178
|
Chris@439
|
179 QString transformUri = result["transform"].value;
|
Chris@439
|
180 QString pluginUri = result["plugin"].value;
|
Chris@439
|
181
|
Chris@439
|
182 QString pluginId = indexer->getIdForPluginURI(pluginUri);
|
Chris@439
|
183 if (pluginId == "") {
|
Chris@439
|
184 cerr << "RDFTransformFactory: WARNING: Unknown plugin <"
|
Chris@439
|
185 << pluginUri.toStdString() << "> for transform <"
|
Chris@440
|
186 << transformUri.toStdString() << ">, skipping this transform"
|
Chris@440
|
187 << endl;
|
Chris@440
|
188 continue;
|
Chris@440
|
189 }
|
Chris@440
|
190
|
Chris@439
|
191 Transform transform;
|
Chris@439
|
192 transform.setPluginIdentifier(pluginId);
|
Chris@439
|
193
|
Chris@489
|
194 if (!setOutput(transform, transformUri)) {
|
Chris@439
|
195 return transforms;
|
Chris@439
|
196 }
|
Chris@439
|
197
|
Chris@489
|
198 if (!setParameters(transform, transformUri)) {
|
Chris@439
|
199 return transforms;
|
Chris@439
|
200 }
|
Chris@439
|
201
|
Chris@440
|
202 uriTransformMap[transformUri] = transform;
|
Chris@439
|
203
|
Chris@489
|
204 // We have to do this a very long way round, to work around
|
Chris@489
|
205 // rasqal's current inability to handle correctly more than one
|
Chris@489
|
206 // OPTIONAL graph in a query
|
Chris@439
|
207
|
Chris@489
|
208 static const char *optionals[] = {
|
Chris@489
|
209 "output",
|
Chris@489
|
210 "program",
|
Chris@489
|
211 "step_size",
|
Chris@489
|
212 "block_size",
|
Chris@489
|
213 "window_type",
|
Chris@489
|
214 "sample_rate",
|
Chris@489
|
215 "start",
|
Chris@489
|
216 "duration"
|
Chris@489
|
217 };
|
Chris@489
|
218
|
Chris@489
|
219 for (int j = 0; j < sizeof(optionals)/sizeof(optionals[0]); ++j) {
|
Chris@439
|
220
|
Chris@489
|
221 QString optional = optionals[j];
|
Chris@489
|
222
|
Chris@489
|
223 QString queryTemplate =
|
Chris@489
|
224 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
|
Chris@489
|
225
|
Chris@489
|
226 " SELECT ?%1 "
|
Chris@489
|
227
|
Chris@489
|
228 " WHERE { "
|
Chris@489
|
229 " <%2> vamp:%1 ?%1 "
|
Chris@489
|
230 " } ";
|
Chris@489
|
231
|
Chris@489
|
232 SimpleSPARQLQuery query
|
Chris@489
|
233 (SimpleSPARQLQuery::QueryFromModel,
|
Chris@489
|
234 queryTemplate.arg(optional).arg(transformUri));
|
Chris@440
|
235
|
Chris@489
|
236 SimpleSPARQLQuery::ResultList results = query.execute();
|
Chris@440
|
237
|
Chris@489
|
238 if (!query.isOK()) {
|
Chris@489
|
239 m_errorString = query.getErrorString();
|
Chris@489
|
240 return transforms;
|
Chris@440
|
241 }
|
Chris@440
|
242
|
Chris@489
|
243 if (results.empty()) continue;
|
Chris@440
|
244
|
Chris@489
|
245 for (int k = 0; k < results.size(); ++k) {
|
Chris@489
|
246
|
Chris@489
|
247 const SimpleSPARQLQuery::Value &v = results[k][optional];
|
Chris@489
|
248
|
Chris@489
|
249 if (v.type == SimpleSPARQLQuery::LiteralValue) {
|
Chris@440
|
250
|
Chris@489
|
251 if (optional == "program") {
|
Chris@489
|
252 transform.setProgram(v.value);
|
Chris@489
|
253 } else if (optional == "step_size") {
|
Chris@489
|
254 transform.setStepSize(v.value.toUInt());
|
Chris@489
|
255 } else if (optional == "block_size") {
|
Chris@489
|
256 transform.setBlockSize(v.value.toUInt());
|
Chris@489
|
257 } else if (optional == "window_type") {
|
Chris@489
|
258 cerr << "NOTE: can't handle window type yet (value is \""
|
Chris@489
|
259 << v.value.toStdString() << "\")" << endl;
|
Chris@489
|
260 } else if (optional == "sample_rate") {
|
Chris@489
|
261 transform.setSampleRate(v.value.toFloat());
|
Chris@489
|
262 } else if (optional == "start") {
|
Chris@489
|
263 transform.setStartTime
|
Chris@489
|
264 (RealTime::fromXsdDuration(v.value.toStdString()));
|
Chris@489
|
265 } else if (optional == "duration") {
|
Chris@489
|
266 transform.setDuration
|
Chris@489
|
267 (RealTime::fromXsdDuration(v.value.toStdString()));
|
Chris@489
|
268 } else {
|
Chris@489
|
269 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional.toStdString() << "\"" << endl;
|
Chris@489
|
270 }
|
Chris@440
|
271 }
|
Chris@440
|
272 }
|
Chris@440
|
273 }
|
Chris@440
|
274
|
Chris@439
|
275 cerr << "RDFTransformFactory: NOTE: Transform is: " << endl;
|
Chris@439
|
276 cerr << transform.toXmlString().toStdString() << endl;
|
Chris@439
|
277
|
Chris@439
|
278 transforms.push_back(transform);
|
Chris@439
|
279 }
|
Chris@439
|
280
|
Chris@439
|
281 return transforms;
|
Chris@439
|
282 }
|
Chris@439
|
283
|
Chris@440
|
284 bool
|
Chris@440
|
285 RDFTransformFactoryImpl::setOutput(Transform &transform,
|
Chris@489
|
286 QString transformUri)
|
Chris@440
|
287 {
|
Chris@489
|
288 SimpleSPARQLQuery::Value outputValue =
|
Chris@489
|
289 SimpleSPARQLQuery::singleResultQuery
|
Chris@489
|
290 (SimpleSPARQLQuery::QueryFromModel,
|
Chris@489
|
291 QString
|
Chris@489
|
292 (
|
Chris@489
|
293 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
|
Chris@489
|
294
|
Chris@489
|
295 " SELECT ?output "
|
Chris@489
|
296
|
Chris@489
|
297 " WHERE { "
|
Chris@489
|
298 " <%1> vamp:output ?output . "
|
Chris@489
|
299 " } "
|
Chris@489
|
300 )
|
Chris@489
|
301 .arg(transformUri),
|
Chris@489
|
302 "output");
|
Chris@489
|
303
|
Chris@489
|
304 if (outputValue.type == SimpleSPARQLQuery::NoValue) {
|
Chris@489
|
305 return true;
|
Chris@489
|
306 }
|
Chris@489
|
307
|
Chris@489
|
308 if (outputValue.type != SimpleSPARQLQuery::URIValue) {
|
Chris@493
|
309 m_errorString = QString("vamp:output given for transform <%1> is not a URI").arg(transformUri);
|
Chris@489
|
310 return false;
|
Chris@489
|
311 }
|
Chris@489
|
312
|
Chris@489
|
313 SimpleSPARQLQuery::Value outputIdValue =
|
Chris@489
|
314 SimpleSPARQLQuery::singleResultQuery
|
Chris@489
|
315 (SimpleSPARQLQuery::QueryFromModel,
|
Chris@480
|
316 QString
|
Chris@440
|
317 (
|
Chris@440
|
318 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
|
Chris@440
|
319
|
Chris@440
|
320 " SELECT ?output_id "
|
Chris@440
|
321
|
Chris@440
|
322 " WHERE { "
|
Chris@489
|
323 " <%1> vamp:identifier ?output_id "
|
Chris@440
|
324 " } "
|
Chris@440
|
325 )
|
Chris@489
|
326 .arg(outputValue.value),
|
Chris@489
|
327 "output_id");
|
Chris@440
|
328
|
Chris@489
|
329 if (outputIdValue.type != SimpleSPARQLQuery::LiteralValue) {
|
Chris@493
|
330 m_errorString = QString("No vamp:identifier found for output <%1>, or vamp:identifier is not a literal").arg(outputValue.value);
|
Chris@440
|
331 return false;
|
Chris@440
|
332 }
|
Chris@489
|
333
|
Chris@489
|
334 transform.setOutput(outputIdValue.value);
|
Chris@440
|
335
|
Chris@440
|
336 return true;
|
Chris@440
|
337 }
|
Chris@440
|
338
|
Chris@440
|
339
|
Chris@440
|
340 bool
|
Chris@440
|
341 RDFTransformFactoryImpl::setParameters(Transform &transform,
|
Chris@489
|
342 QString transformUri)
|
Chris@440
|
343 {
|
Chris@440
|
344 SimpleSPARQLQuery paramQuery
|
Chris@489
|
345 (SimpleSPARQLQuery::QueryFromModel,
|
Chris@480
|
346 QString
|
Chris@440
|
347 (
|
Chris@440
|
348 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
|
Chris@440
|
349
|
Chris@440
|
350 " SELECT ?param_id ?param_value "
|
Chris@440
|
351
|
Chris@440
|
352 " WHERE { "
|
Chris@489
|
353 " <%1> vamp:parameter_binding ?binding . "
|
Chris@440
|
354 " ?binding vamp:parameter ?param ; "
|
Chris@440
|
355 " vamp:value ?param_value . "
|
Chris@440
|
356 " ?param vamp:identifier ?param_id "
|
Chris@440
|
357 " } "
|
Chris@440
|
358 )
|
Chris@440
|
359 .arg(transformUri));
|
Chris@440
|
360
|
Chris@440
|
361 SimpleSPARQLQuery::ResultList paramResults = paramQuery.execute();
|
Chris@440
|
362
|
Chris@440
|
363 if (!paramQuery.isOK()) {
|
Chris@440
|
364 m_errorString = paramQuery.getErrorString();
|
Chris@440
|
365 return false;
|
Chris@440
|
366 }
|
Chris@440
|
367
|
Chris@440
|
368 if (paramQuery.wasCancelled()) {
|
Chris@440
|
369 m_errorString = "Query cancelled";
|
Chris@440
|
370 return false;
|
Chris@440
|
371 }
|
Chris@440
|
372
|
Chris@440
|
373 for (int j = 0; j < paramResults.size(); ++j) {
|
Chris@440
|
374
|
Chris@440
|
375 QString paramId = paramResults[j]["param_id"].value;
|
Chris@440
|
376 QString paramValue = paramResults[j]["param_value"].value;
|
Chris@440
|
377
|
Chris@440
|
378 if (paramId == "" || paramValue == "") continue;
|
Chris@440
|
379
|
Chris@440
|
380 transform.setParameter(paramId, paramValue.toFloat());
|
Chris@440
|
381 }
|
Chris@440
|
382
|
Chris@440
|
383 return true;
|
Chris@440
|
384 }
|
Chris@440
|
385
|