annotate rdf/RDFTransformFactory.cpp @ 695:7365a0bd7d5a library_integration

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