annotate rdf/RDFTransformFactory.cpp @ 503:3176aade1a03

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