annotate rdf/RDFTransformFactory.cpp @ 489:82ab61fa9223

* Reorganise our sparql queries on the basis that Redland must be available, not only optional. So for anything querying the pool of data about plugins, we use a single datastore and model which is initialised at the outset by PluginRDFIndexer and then queried directly; for anything that "reads from a file" (e.g. loading annotations) we query directly using Rasqal, going to the datastore when we need additional plugin-related information. This may improve performance, but mostly it simplifies the code and fixes a serious issue with RDF import in the previous versions (namely that multiple sequential RDF imports would end up sharing the same RDF data pool!)
author Chris Cannam
date Fri, 21 Nov 2008 16:12:29 +0000
parents 3ffce691c9bf
children 23945cdd7161
rev   line source
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@439 42 bool isOK();
Chris@439 43 QString getErrorString() const;
Chris@439 44
Chris@439 45 std::vector<Transform> getTransforms(ProgressReporter *);
Chris@439 46
Chris@439 47 protected:
Chris@439 48 QString m_urlString;
Chris@439 49 QString m_errorString;
Chris@489 50 bool setOutput(Transform &, QString);
Chris@489 51 bool setParameters(Transform &, QString);
Chris@439 52 };
Chris@439 53
Chris@439 54
Chris@439 55 QString
Chris@439 56 RDFTransformFactory::getKnownExtensions()
Chris@439 57 {
Chris@439 58 return "*.rdf *.n3 *.ttl";
Chris@439 59 }
Chris@439 60
Chris@439 61 RDFTransformFactory::RDFTransformFactory(QString url) :
Chris@439 62 m_d(new RDFTransformFactoryImpl(url))
Chris@439 63 {
Chris@439 64 }
Chris@439 65
Chris@439 66 RDFTransformFactory::~RDFTransformFactory()
Chris@439 67 {
Chris@439 68 delete m_d;
Chris@439 69 }
Chris@439 70
Chris@439 71 bool
Chris@439 72 RDFTransformFactory::isOK()
Chris@439 73 {
Chris@439 74 return m_d->isOK();
Chris@439 75 }
Chris@439 76
Chris@439 77 QString
Chris@439 78 RDFTransformFactory::getErrorString() const
Chris@439 79 {
Chris@439 80 return m_d->getErrorString();
Chris@439 81 }
Chris@439 82
Chris@439 83 std::vector<Transform>
Chris@439 84 RDFTransformFactory::getTransforms(ProgressReporter *r)
Chris@439 85 {
Chris@439 86 return m_d->getTransforms(r);
Chris@439 87 }
Chris@439 88
Chris@439 89 RDFTransformFactoryImpl::RDFTransformFactoryImpl(QString url) :
Chris@439 90 m_urlString(url)
Chris@439 91 {
Chris@439 92 }
Chris@439 93
Chris@439 94 RDFTransformFactoryImpl::~RDFTransformFactoryImpl()
Chris@439 95 {
Chris@439 96 }
Chris@439 97
Chris@439 98 bool
Chris@439 99 RDFTransformFactoryImpl::isOK()
Chris@439 100 {
Chris@439 101 return (m_errorString == "");
Chris@439 102 }
Chris@439 103
Chris@439 104 QString
Chris@439 105 RDFTransformFactoryImpl::getErrorString() const
Chris@439 106 {
Chris@439 107 return m_errorString;
Chris@439 108 }
Chris@439 109
Chris@439 110 std::vector<Transform>
Chris@439 111 RDFTransformFactoryImpl::getTransforms(ProgressReporter *reporter)
Chris@439 112 {
Chris@439 113 std::vector<Transform> transforms;
Chris@439 114
Chris@440 115 std::map<QString, Transform> uriTransformMap;
Chris@439 116
Chris@489 117 QString query =
Chris@440 118 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@439 119
Chris@489 120 " SELECT ?transform ?plugin "
Chris@440 121
Chris@440 122 " FROM <%2> "
Chris@439 123
Chris@440 124 " WHERE { "
Chris@440 125 " ?transform a vamp:Transform ; "
Chris@440 126 " vamp:plugin ?plugin . "
Chris@440 127 " } ";
Chris@440 128
Chris@440 129 SimpleSPARQLQuery transformsQuery
Chris@489 130 (SimpleSPARQLQuery::QueryFromSingleSource, query.arg(m_urlString));
Chris@440 131
Chris@440 132 SimpleSPARQLQuery::ResultList transformResults = transformsQuery.execute();
Chris@440 133
Chris@440 134 if (!transformsQuery.isOK()) {
Chris@440 135 m_errorString = transformsQuery.getErrorString();
Chris@439 136 return transforms;
Chris@439 137 }
Chris@439 138
Chris@440 139 if (transformResults.empty()) {
Chris@440 140 cerr << "RDFTransformFactory: NOTE: No RDF/TTL transform descriptions found in document at <" << m_urlString.toStdString() << ">" << endl;
Chris@439 141 return transforms;
Chris@439 142 }
Chris@439 143
Chris@489 144 // There are various queries we need to make that might include
Chris@489 145 // data from iether the transform RDF or the model accumulated
Chris@489 146 // from plugin descriptions. For example, the transform RDF may
Chris@489 147 // specify the output's true URI, or it might have a blank node or
Chris@489 148 // some other URI with the appropriate vamp:identifier included in
Chris@489 149 // the file. To cover both cases, we need to add the file itself
Chris@489 150 // into the model and always query the model using the transform
Chris@489 151 // URI rather than querying the file itself subsequently.
Chris@489 152
Chris@489 153 SimpleSPARQLQuery::addSourceToModel(m_urlString);
Chris@489 154
Chris@439 155 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
Chris@439 156
Chris@440 157 for (int i = 0; i < transformResults.size(); ++i) {
Chris@439 158
Chris@440 159 SimpleSPARQLQuery::KeyValueMap &result = transformResults[i];
Chris@439 160
Chris@439 161 QString transformUri = result["transform"].value;
Chris@439 162 QString pluginUri = result["plugin"].value;
Chris@439 163
Chris@439 164 QString pluginId = indexer->getIdForPluginURI(pluginUri);
Chris@439 165 if (pluginId == "") {
Chris@439 166 cerr << "RDFTransformFactory: WARNING: Unknown plugin <"
Chris@439 167 << pluginUri.toStdString() << "> for transform <"
Chris@440 168 << transformUri.toStdString() << ">, skipping this transform"
Chris@440 169 << endl;
Chris@440 170 continue;
Chris@440 171 }
Chris@440 172
Chris@439 173 Transform transform;
Chris@439 174 transform.setPluginIdentifier(pluginId);
Chris@439 175
Chris@489 176 if (!setOutput(transform, transformUri)) {
Chris@439 177 return transforms;
Chris@439 178 }
Chris@439 179
Chris@489 180 if (!setParameters(transform, transformUri)) {
Chris@439 181 return transforms;
Chris@439 182 }
Chris@439 183
Chris@440 184 uriTransformMap[transformUri] = transform;
Chris@439 185
Chris@489 186 // We have to do this a very long way round, to work around
Chris@489 187 // rasqal's current inability to handle correctly more than one
Chris@489 188 // OPTIONAL graph in a query
Chris@439 189
Chris@489 190 static const char *optionals[] = {
Chris@489 191 "output",
Chris@489 192 "program",
Chris@489 193 "step_size",
Chris@489 194 "block_size",
Chris@489 195 "window_type",
Chris@489 196 "sample_rate",
Chris@489 197 "start",
Chris@489 198 "duration"
Chris@489 199 };
Chris@489 200
Chris@489 201 for (int j = 0; j < sizeof(optionals)/sizeof(optionals[0]); ++j) {
Chris@439 202
Chris@489 203 QString optional = optionals[j];
Chris@489 204
Chris@489 205 QString queryTemplate =
Chris@489 206 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@489 207
Chris@489 208 " SELECT ?%1 "
Chris@489 209
Chris@489 210 " WHERE { "
Chris@489 211 " <%2> vamp:%1 ?%1 "
Chris@489 212 " } ";
Chris@489 213
Chris@489 214 SimpleSPARQLQuery query
Chris@489 215 (SimpleSPARQLQuery::QueryFromModel,
Chris@489 216 queryTemplate.arg(optional).arg(transformUri));
Chris@440 217
Chris@489 218 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@440 219
Chris@489 220 if (!query.isOK()) {
Chris@489 221 m_errorString = query.getErrorString();
Chris@489 222 return transforms;
Chris@440 223 }
Chris@440 224
Chris@489 225 if (results.empty()) continue;
Chris@440 226
Chris@489 227 for (int k = 0; k < results.size(); ++k) {
Chris@489 228
Chris@489 229 const SimpleSPARQLQuery::Value &v = results[k][optional];
Chris@489 230
Chris@489 231 if (v.type == SimpleSPARQLQuery::LiteralValue) {
Chris@440 232
Chris@489 233 if (optional == "program") {
Chris@489 234 transform.setProgram(v.value);
Chris@489 235 } else if (optional == "step_size") {
Chris@489 236 transform.setStepSize(v.value.toUInt());
Chris@489 237 } else if (optional == "block_size") {
Chris@489 238 transform.setBlockSize(v.value.toUInt());
Chris@489 239 } else if (optional == "window_type") {
Chris@489 240 cerr << "NOTE: can't handle window type yet (value is \""
Chris@489 241 << v.value.toStdString() << "\")" << endl;
Chris@489 242 } else if (optional == "sample_rate") {
Chris@489 243 transform.setSampleRate(v.value.toFloat());
Chris@489 244 } else if (optional == "start") {
Chris@489 245 transform.setStartTime
Chris@489 246 (RealTime::fromXsdDuration(v.value.toStdString()));
Chris@489 247 } else if (optional == "duration") {
Chris@489 248 transform.setDuration
Chris@489 249 (RealTime::fromXsdDuration(v.value.toStdString()));
Chris@489 250 } else {
Chris@489 251 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional.toStdString() << "\"" << endl;
Chris@489 252 }
Chris@440 253 }
Chris@440 254 }
Chris@440 255 }
Chris@440 256
Chris@439 257 cerr << "RDFTransformFactory: NOTE: Transform is: " << endl;
Chris@439 258 cerr << transform.toXmlString().toStdString() << endl;
Chris@439 259
Chris@439 260 transforms.push_back(transform);
Chris@439 261 }
Chris@439 262
Chris@439 263 return transforms;
Chris@439 264 }
Chris@439 265
Chris@440 266 bool
Chris@440 267 RDFTransformFactoryImpl::setOutput(Transform &transform,
Chris@489 268 QString transformUri)
Chris@440 269 {
Chris@489 270 SimpleSPARQLQuery::Value outputValue =
Chris@489 271 SimpleSPARQLQuery::singleResultQuery
Chris@489 272 (SimpleSPARQLQuery::QueryFromModel,
Chris@489 273 QString
Chris@489 274 (
Chris@489 275 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@489 276
Chris@489 277 " SELECT ?output "
Chris@489 278
Chris@489 279 " WHERE { "
Chris@489 280 " <%1> vamp:output ?output . "
Chris@489 281 " } "
Chris@489 282 )
Chris@489 283 .arg(transformUri),
Chris@489 284 "output");
Chris@489 285
Chris@489 286 if (outputValue.type == SimpleSPARQLQuery::NoValue) {
Chris@489 287 return true;
Chris@489 288 }
Chris@489 289
Chris@489 290 if (outputValue.type != SimpleSPARQLQuery::URIValue) {
Chris@489 291 m_errorString = "No vamp:output given, or not a URI";
Chris@489 292 return false;
Chris@489 293 }
Chris@489 294
Chris@489 295 SimpleSPARQLQuery::Value outputIdValue =
Chris@489 296 SimpleSPARQLQuery::singleResultQuery
Chris@489 297 (SimpleSPARQLQuery::QueryFromModel,
Chris@480 298 QString
Chris@440 299 (
Chris@440 300 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@440 301
Chris@440 302 " SELECT ?output_id "
Chris@440 303
Chris@440 304 " WHERE { "
Chris@489 305 " <%1> vamp:identifier ?output_id "
Chris@440 306 " } "
Chris@440 307 )
Chris@489 308 .arg(outputValue.value),
Chris@489 309 "output_id");
Chris@440 310
Chris@489 311 if (outputIdValue.type != SimpleSPARQLQuery::LiteralValue) {
Chris@489 312 m_errorString = "No output vamp:identifier available, or not a literal";
Chris@440 313 return false;
Chris@440 314 }
Chris@489 315
Chris@489 316 transform.setOutput(outputIdValue.value);
Chris@440 317
Chris@440 318 return true;
Chris@440 319 }
Chris@440 320
Chris@440 321
Chris@440 322 bool
Chris@440 323 RDFTransformFactoryImpl::setParameters(Transform &transform,
Chris@489 324 QString transformUri)
Chris@440 325 {
Chris@440 326 SimpleSPARQLQuery paramQuery
Chris@489 327 (SimpleSPARQLQuery::QueryFromModel,
Chris@480 328 QString
Chris@440 329 (
Chris@440 330 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@440 331
Chris@440 332 " SELECT ?param_id ?param_value "
Chris@440 333
Chris@440 334 " WHERE { "
Chris@489 335 " <%1> vamp:parameter_binding ?binding . "
Chris@440 336 " ?binding vamp:parameter ?param ; "
Chris@440 337 " vamp:value ?param_value . "
Chris@440 338 " ?param vamp:identifier ?param_id "
Chris@440 339 " } "
Chris@440 340 )
Chris@440 341 .arg(transformUri));
Chris@440 342
Chris@440 343 SimpleSPARQLQuery::ResultList paramResults = paramQuery.execute();
Chris@440 344
Chris@440 345 if (!paramQuery.isOK()) {
Chris@440 346 m_errorString = paramQuery.getErrorString();
Chris@440 347 return false;
Chris@440 348 }
Chris@440 349
Chris@440 350 if (paramQuery.wasCancelled()) {
Chris@440 351 m_errorString = "Query cancelled";
Chris@440 352 return false;
Chris@440 353 }
Chris@440 354
Chris@440 355 for (int j = 0; j < paramResults.size(); ++j) {
Chris@440 356
Chris@440 357 QString paramId = paramResults[j]["param_id"].value;
Chris@440 358 QString paramValue = paramResults[j]["param_value"].value;
Chris@440 359
Chris@440 360 if (paramId == "" || paramValue == "") continue;
Chris@440 361
Chris@440 362 transform.setParameter(paramId, paramValue.toFloat());
Chris@440 363 }
Chris@440 364
Chris@440 365 return true;
Chris@440 366 }
Chris@440 367