annotate rdf/RDFTransformFactory.cpp @ 718:f3fd2988fc9b

Fix incorrect query structure for output type URIs. This led to some output RDF features being written with type URIs intended for different outputs. Also revert some SVDEBUGs to cerrs -- they are intended as user-visible errors or warnings rather than debug
author Chris Cannam
date Mon, 09 Jan 2012 16:28:54 +0000
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