annotate rdf/RDFTransformFactory.cpp @ 497:b6dc6c7f402c

Various fixes: * Fix handling of HTTP redirects (avoiding crashes... I hope) * Fix failure to delete FFT models when a feature extraction model transformer was abandoned (also a cause of crashes in the past) * Fix deadlock when said transform was abandoned before its source model was ready because the session was being cleared (and so the source model would never be ready)
author Chris Cannam
date Fri, 28 Nov 2008 13:36:13 +0000
parents 81963c51b488
children 3176aade1a03
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@494 21 #include <QTextStream>
Chris@494 22
Chris@439 23 #include <iostream>
Chris@439 24 #include <cmath>
Chris@439 25
Chris@439 26 #include "SimpleSPARQLQuery.h"
Chris@439 27 #include "PluginRDFIndexer.h"
Chris@494 28 #include "PluginRDFDescription.h"
Chris@439 29 #include "base/ProgressReporter.h"
Chris@439 30
Chris@439 31 #include "transform/TransformFactory.h"
Chris@439 32
Chris@439 33 using std::cerr;
Chris@439 34 using std::endl;
Chris@439 35
Chris@439 36 typedef const unsigned char *STR; // redland's expected string type
Chris@439 37
Chris@439 38
Chris@439 39 class RDFTransformFactoryImpl
Chris@439 40 {
Chris@439 41 public:
Chris@439 42 RDFTransformFactoryImpl(QString url);
Chris@439 43 virtual ~RDFTransformFactoryImpl();
Chris@439 44
Chris@493 45 bool isRDF();
Chris@439 46 bool isOK();
Chris@439 47 QString getErrorString() const;
Chris@439 48
Chris@439 49 std::vector<Transform> getTransforms(ProgressReporter *);
Chris@439 50
Chris@494 51 static QString writeTransformToRDF(const Transform &, QString);
Chris@494 52
Chris@439 53 protected:
Chris@439 54 QString m_urlString;
Chris@439 55 QString m_errorString;
Chris@493 56 bool m_isRDF;
Chris@489 57 bool setOutput(Transform &, QString);
Chris@489 58 bool setParameters(Transform &, QString);
Chris@439 59 };
Chris@439 60
Chris@439 61
Chris@439 62 QString
Chris@439 63 RDFTransformFactory::getKnownExtensions()
Chris@439 64 {
Chris@439 65 return "*.rdf *.n3 *.ttl";
Chris@439 66 }
Chris@439 67
Chris@439 68 RDFTransformFactory::RDFTransformFactory(QString url) :
Chris@439 69 m_d(new RDFTransformFactoryImpl(url))
Chris@439 70 {
Chris@439 71 }
Chris@439 72
Chris@439 73 RDFTransformFactory::~RDFTransformFactory()
Chris@439 74 {
Chris@439 75 delete m_d;
Chris@439 76 }
Chris@439 77
Chris@439 78 bool
Chris@493 79 RDFTransformFactory::isRDF()
Chris@493 80 {
Chris@493 81 return m_d->isRDF();
Chris@493 82 }
Chris@493 83
Chris@493 84 bool
Chris@439 85 RDFTransformFactory::isOK()
Chris@439 86 {
Chris@439 87 return m_d->isOK();
Chris@439 88 }
Chris@439 89
Chris@439 90 QString
Chris@439 91 RDFTransformFactory::getErrorString() const
Chris@439 92 {
Chris@439 93 return m_d->getErrorString();
Chris@439 94 }
Chris@439 95
Chris@439 96 std::vector<Transform>
Chris@439 97 RDFTransformFactory::getTransforms(ProgressReporter *r)
Chris@439 98 {
Chris@439 99 return m_d->getTransforms(r);
Chris@439 100 }
Chris@439 101
Chris@494 102 QString
Chris@494 103 RDFTransformFactory::writeTransformToRDF(const Transform &t, QString f)
Chris@494 104 {
Chris@494 105 return RDFTransformFactoryImpl::writeTransformToRDF(t, f);
Chris@494 106 }
Chris@494 107
Chris@439 108 RDFTransformFactoryImpl::RDFTransformFactoryImpl(QString url) :
Chris@493 109 m_urlString(url),
Chris@493 110 m_isRDF(false)
Chris@439 111 {
Chris@439 112 }
Chris@439 113
Chris@439 114 RDFTransformFactoryImpl::~RDFTransformFactoryImpl()
Chris@439 115 {
Chris@492 116 SimpleSPARQLQuery::closeSingleSource(m_urlString);
Chris@439 117 }
Chris@439 118
Chris@439 119 bool
Chris@493 120 RDFTransformFactoryImpl::isRDF()
Chris@493 121 {
Chris@493 122 return m_isRDF;
Chris@493 123 }
Chris@493 124
Chris@493 125 bool
Chris@439 126 RDFTransformFactoryImpl::isOK()
Chris@439 127 {
Chris@439 128 return (m_errorString == "");
Chris@439 129 }
Chris@439 130
Chris@439 131 QString
Chris@439 132 RDFTransformFactoryImpl::getErrorString() const
Chris@439 133 {
Chris@439 134 return m_errorString;
Chris@439 135 }
Chris@439 136
Chris@439 137 std::vector<Transform>
Chris@439 138 RDFTransformFactoryImpl::getTransforms(ProgressReporter *reporter)
Chris@439 139 {
Chris@439 140 std::vector<Transform> transforms;
Chris@439 141
Chris@440 142 std::map<QString, Transform> uriTransformMap;
Chris@439 143
Chris@489 144 QString query =
Chris@440 145 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@439 146
Chris@489 147 " SELECT ?transform ?plugin "
Chris@440 148
Chris@440 149 " FROM <%2> "
Chris@439 150
Chris@440 151 " WHERE { "
Chris@440 152 " ?transform a vamp:Transform ; "
Chris@440 153 " vamp:plugin ?plugin . "
Chris@440 154 " } ";
Chris@440 155
Chris@440 156 SimpleSPARQLQuery transformsQuery
Chris@489 157 (SimpleSPARQLQuery::QueryFromSingleSource, query.arg(m_urlString));
Chris@440 158
Chris@440 159 SimpleSPARQLQuery::ResultList transformResults = transformsQuery.execute();
Chris@440 160
Chris@440 161 if (!transformsQuery.isOK()) {
Chris@440 162 m_errorString = transformsQuery.getErrorString();
Chris@439 163 return transforms;
Chris@439 164 }
Chris@439 165
Chris@493 166 m_isRDF = true;
Chris@493 167
Chris@440 168 if (transformResults.empty()) {
Chris@440 169 cerr << "RDFTransformFactory: NOTE: No RDF/TTL transform descriptions found in document at <" << m_urlString.toStdString() << ">" << endl;
Chris@439 170 return transforms;
Chris@439 171 }
Chris@439 172
Chris@489 173 // There are various queries we need to make that might include
Chris@494 174 // data from either the transform RDF or the model accumulated
Chris@489 175 // from plugin descriptions. For example, the transform RDF may
Chris@489 176 // specify the output's true URI, or it might have a blank node or
Chris@489 177 // some other URI with the appropriate vamp:identifier included in
Chris@489 178 // the file. To cover both cases, we need to add the file itself
Chris@489 179 // into the model and always query the model using the transform
Chris@489 180 // URI rather than querying the file itself subsequently.
Chris@489 181
Chris@489 182 SimpleSPARQLQuery::addSourceToModel(m_urlString);
Chris@489 183
Chris@439 184 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
Chris@439 185
Chris@440 186 for (int i = 0; i < transformResults.size(); ++i) {
Chris@439 187
Chris@440 188 SimpleSPARQLQuery::KeyValueMap &result = transformResults[i];
Chris@439 189
Chris@439 190 QString transformUri = result["transform"].value;
Chris@439 191 QString pluginUri = result["plugin"].value;
Chris@439 192
Chris@439 193 QString pluginId = indexer->getIdForPluginURI(pluginUri);
Chris@439 194 if (pluginId == "") {
Chris@439 195 cerr << "RDFTransformFactory: WARNING: Unknown plugin <"
Chris@439 196 << pluginUri.toStdString() << "> for transform <"
Chris@440 197 << transformUri.toStdString() << ">, skipping this transform"
Chris@440 198 << endl;
Chris@440 199 continue;
Chris@440 200 }
Chris@440 201
Chris@439 202 Transform transform;
Chris@439 203 transform.setPluginIdentifier(pluginId);
Chris@439 204
Chris@489 205 if (!setOutput(transform, transformUri)) {
Chris@439 206 return transforms;
Chris@439 207 }
Chris@439 208
Chris@489 209 if (!setParameters(transform, transformUri)) {
Chris@439 210 return transforms;
Chris@439 211 }
Chris@439 212
Chris@440 213 uriTransformMap[transformUri] = transform;
Chris@439 214
Chris@489 215 // We have to do this a very long way round, to work around
Chris@489 216 // rasqal's current inability to handle correctly more than one
Chris@489 217 // OPTIONAL graph in a query
Chris@439 218
Chris@489 219 static const char *optionals[] = {
Chris@489 220 "output",
Chris@489 221 "program",
Chris@489 222 "step_size",
Chris@489 223 "block_size",
Chris@489 224 "window_type",
Chris@489 225 "sample_rate",
Chris@489 226 "start",
Chris@489 227 "duration"
Chris@489 228 };
Chris@489 229
Chris@489 230 for (int j = 0; j < sizeof(optionals)/sizeof(optionals[0]); ++j) {
Chris@439 231
Chris@489 232 QString optional = optionals[j];
Chris@489 233
Chris@489 234 QString queryTemplate =
Chris@489 235 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@489 236
Chris@489 237 " SELECT ?%1 "
Chris@489 238
Chris@489 239 " WHERE { "
Chris@489 240 " <%2> vamp:%1 ?%1 "
Chris@489 241 " } ";
Chris@489 242
Chris@489 243 SimpleSPARQLQuery query
Chris@489 244 (SimpleSPARQLQuery::QueryFromModel,
Chris@489 245 queryTemplate.arg(optional).arg(transformUri));
Chris@440 246
Chris@489 247 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@440 248
Chris@489 249 if (!query.isOK()) {
Chris@489 250 m_errorString = query.getErrorString();
Chris@489 251 return transforms;
Chris@440 252 }
Chris@440 253
Chris@489 254 if (results.empty()) continue;
Chris@440 255
Chris@489 256 for (int k = 0; k < results.size(); ++k) {
Chris@489 257
Chris@489 258 const SimpleSPARQLQuery::Value &v = results[k][optional];
Chris@489 259
Chris@489 260 if (v.type == SimpleSPARQLQuery::LiteralValue) {
Chris@440 261
Chris@489 262 if (optional == "program") {
Chris@489 263 transform.setProgram(v.value);
Chris@489 264 } else if (optional == "step_size") {
Chris@489 265 transform.setStepSize(v.value.toUInt());
Chris@489 266 } else if (optional == "block_size") {
Chris@489 267 transform.setBlockSize(v.value.toUInt());
Chris@489 268 } else if (optional == "window_type") {
Chris@489 269 cerr << "NOTE: can't handle window type yet (value is \""
Chris@489 270 << v.value.toStdString() << "\")" << endl;
Chris@489 271 } else if (optional == "sample_rate") {
Chris@489 272 transform.setSampleRate(v.value.toFloat());
Chris@489 273 } else if (optional == "start") {
Chris@489 274 transform.setStartTime
Chris@489 275 (RealTime::fromXsdDuration(v.value.toStdString()));
Chris@489 276 } else if (optional == "duration") {
Chris@489 277 transform.setDuration
Chris@489 278 (RealTime::fromXsdDuration(v.value.toStdString()));
Chris@489 279 } else {
Chris@489 280 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional.toStdString() << "\"" << endl;
Chris@489 281 }
Chris@440 282 }
Chris@440 283 }
Chris@440 284 }
Chris@440 285
Chris@439 286 cerr << "RDFTransformFactory: NOTE: Transform is: " << endl;
Chris@439 287 cerr << transform.toXmlString().toStdString() << endl;
Chris@439 288
Chris@439 289 transforms.push_back(transform);
Chris@439 290 }
Chris@439 291
Chris@439 292 return transforms;
Chris@439 293 }
Chris@439 294
Chris@440 295 bool
Chris@440 296 RDFTransformFactoryImpl::setOutput(Transform &transform,
Chris@489 297 QString transformUri)
Chris@440 298 {
Chris@489 299 SimpleSPARQLQuery::Value outputValue =
Chris@489 300 SimpleSPARQLQuery::singleResultQuery
Chris@489 301 (SimpleSPARQLQuery::QueryFromModel,
Chris@489 302 QString
Chris@489 303 (
Chris@489 304 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@489 305
Chris@494 306 " SELECT ?output_id "
Chris@489 307
Chris@489 308 " WHERE { "
Chris@489 309 " <%1> vamp:output ?output . "
Chris@494 310 " ?output vamp:identifier ?output_id "
Chris@489 311 " } "
Chris@489 312 )
Chris@489 313 .arg(transformUri),
Chris@494 314 "output_id");
Chris@489 315
Chris@489 316 if (outputValue.type == SimpleSPARQLQuery::NoValue) {
Chris@489 317 return true;
Chris@489 318 }
Chris@494 319
Chris@494 320 if (outputValue.type != SimpleSPARQLQuery::LiteralValue) {
Chris@494 321 m_errorString = QString("No vamp:identifier found for output of transform <%1>, or vamp:identifier is not a literal").arg(transformUri);
Chris@489 322 return false;
Chris@489 323 }
Chris@489 324
Chris@494 325 transform.setOutput(outputValue.value);
Chris@440 326
Chris@440 327 return true;
Chris@440 328 }
Chris@440 329
Chris@440 330
Chris@440 331 bool
Chris@440 332 RDFTransformFactoryImpl::setParameters(Transform &transform,
Chris@489 333 QString transformUri)
Chris@440 334 {
Chris@440 335 SimpleSPARQLQuery paramQuery
Chris@489 336 (SimpleSPARQLQuery::QueryFromModel,
Chris@480 337 QString
Chris@440 338 (
Chris@440 339 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@440 340
Chris@440 341 " SELECT ?param_id ?param_value "
Chris@440 342
Chris@440 343 " WHERE { "
Chris@489 344 " <%1> vamp:parameter_binding ?binding . "
Chris@440 345 " ?binding vamp:parameter ?param ; "
Chris@440 346 " vamp:value ?param_value . "
Chris@440 347 " ?param vamp:identifier ?param_id "
Chris@440 348 " } "
Chris@440 349 )
Chris@440 350 .arg(transformUri));
Chris@440 351
Chris@440 352 SimpleSPARQLQuery::ResultList paramResults = paramQuery.execute();
Chris@440 353
Chris@440 354 if (!paramQuery.isOK()) {
Chris@440 355 m_errorString = paramQuery.getErrorString();
Chris@440 356 return false;
Chris@440 357 }
Chris@440 358
Chris@440 359 if (paramQuery.wasCancelled()) {
Chris@440 360 m_errorString = "Query cancelled";
Chris@440 361 return false;
Chris@440 362 }
Chris@440 363
Chris@440 364 for (int j = 0; j < paramResults.size(); ++j) {
Chris@440 365
Chris@440 366 QString paramId = paramResults[j]["param_id"].value;
Chris@440 367 QString paramValue = paramResults[j]["param_value"].value;
Chris@440 368
Chris@440 369 if (paramId == "" || paramValue == "") continue;
Chris@440 370
Chris@440 371 transform.setParameter(paramId, paramValue.toFloat());
Chris@440 372 }
Chris@440 373
Chris@440 374 return true;
Chris@440 375 }
Chris@440 376
Chris@494 377 QString
Chris@494 378 RDFTransformFactoryImpl::writeTransformToRDF(const Transform &transform,
Chris@494 379 QString uri)
Chris@494 380 {
Chris@494 381 QString str;
Chris@494 382 QTextStream s(&str);
Chris@494 383
Chris@494 384 // assumes the usual prefixes are available
Chris@494 385
Chris@494 386 s << uri << " a vamp:Transform ;" << endl;
Chris@494 387
Chris@494 388 QString pluginId = transform.getPluginIdentifier();
Chris@494 389 QString pluginUri = PluginRDFIndexer::getInstance()->getURIForPluginId(pluginId);
Chris@494 390
Chris@494 391 PluginRDFDescription description(pluginId);
Chris@494 392 QString outputUri = description.getOutputUri(transform.getOutput());
Chris@494 393
Chris@494 394 if (transform.getOutput() != "" && outputUri == "") {
Chris@494 395 std::cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No output URI available for transform output id \"" << transform.getOutput().toStdString() << "\"" << std::endl;
Chris@494 396 }
Chris@494 397
Chris@494 398 s << " vamp:plugin <" << pluginUri << "> ;" << endl;
Chris@494 399
Chris@494 400 if (transform.getStepSize() != 0) {
Chris@494 401 s << " vamp:step_size \"" << transform.getStepSize() << "\"^^xsd:int ; " << endl;
Chris@494 402 }
Chris@494 403 if (transform.getBlockSize() != 0) {
Chris@494 404 s << " vamp:block_size \"" << transform.getBlockSize() << "\"^^xsd:int ; " << endl;
Chris@494 405 }
Chris@494 406 if (transform.getStartTime() != RealTime::zeroTime) {
Chris@494 407 s << " vamp:start \"" << transform.getStartTime().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 408 }
Chris@494 409 if (transform.getDuration() != RealTime::zeroTime) {
Chris@494 410 s << " vamp:duration \"" << transform.getDuration().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 411 }
Chris@494 412 if (transform.getSampleRate() != 0) {
Chris@494 413 s << " vamp:sample_rate \"" << transform.getSampleRate() << "\"^^xsd:float ; " << endl;
Chris@494 414 }
Chris@494 415
Chris@494 416 QString program = transform.getProgram();
Chris@494 417
Chris@494 418 if (program != "") {
Chris@494 419 s << " vamp:program \"\"\"" << program << "\"\"\" ;" << endl;
Chris@494 420 }
Chris@494 421
Chris@494 422 Transform::ParameterMap parameters = transform.getParameters();
Chris@494 423 for (Transform::ParameterMap::const_iterator i = parameters.begin();
Chris@494 424 i != parameters.end(); ++i) {
Chris@494 425 QString name = i->first;
Chris@494 426 float value = i->second;
Chris@494 427 s << " vamp:parameter_binding [" << endl;
Chris@494 428 s << " vamp:parameter [ vamp:identifier \"" << name << "\" ] ;" << endl;
Chris@494 429 s << " vamp:value \"" << value << "\"^^xsd:float ;" << endl;
Chris@494 430 s << " ] ;" << endl;
Chris@494 431 }
Chris@494 432
Chris@494 433 if (outputUri != "") {
Chris@494 434 s << " vamp:output <" << outputUri << "> ." << endl;
Chris@494 435 } else {
Chris@494 436 s << " ." << endl;
Chris@494 437 }
Chris@494 438
Chris@494 439 return str;
Chris@494 440 }
Chris@494 441