annotate rdf/RDFTransformFactory.cpp @ 661:a4faa1840384

* If a FileSource URL won't convert at all in strict mode, try again in tolerant mode (necessary for e.g. filenames with square brackets in them)
author Chris Cannam
date Tue, 19 Oct 2010 21:47:55 +0100
parents 18488253a3f4
children b4a8d8221eaf be43b2fe68e8
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@440 171 cerr << "RDFTransformFactory: NOTE: No RDF/TTL transform descriptions found in document at <" << m_urlString.toStdString() << ">" << 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@439 198 << pluginUri.toStdString() << "> for transform <"
Chris@440 199 << transformUri.toStdString() << ">, 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@489 275 cerr << "NOTE: can't handle window type yet (value is \""
Chris@489 276 << v.value.toStdString() << "\")" << endl;
Chris@489 277 } else if (optional == "sample_rate") {
Chris@489 278 transform.setSampleRate(v.value.toFloat());
Chris@489 279 } else if (optional == "start") {
Chris@489 280 transform.setStartTime
Chris@489 281 (RealTime::fromXsdDuration(v.value.toStdString()));
Chris@489 282 } else if (optional == "duration") {
Chris@489 283 transform.setDuration
Chris@489 284 (RealTime::fromXsdDuration(v.value.toStdString()));
Chris@489 285 } else {
Chris@489 286 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional.toStdString() << "\"" << endl;
Chris@489 287 }
Chris@440 288 }
Chris@440 289 }
Chris@440 290 }
Chris@440 291
Chris@439 292 cerr << "RDFTransformFactory: NOTE: Transform is: " << endl;
Chris@439 293 cerr << transform.toXmlString().toStdString() << endl;
Chris@439 294
Chris@439 295 transforms.push_back(transform);
Chris@439 296 }
Chris@439 297
Chris@439 298 return transforms;
Chris@439 299 }
Chris@439 300
Chris@440 301 bool
Chris@440 302 RDFTransformFactoryImpl::setOutput(Transform &transform,
Chris@489 303 QString transformUri)
Chris@440 304 {
Chris@489 305 SimpleSPARQLQuery::Value outputValue =
Chris@489 306 SimpleSPARQLQuery::singleResultQuery
Chris@489 307 (SimpleSPARQLQuery::QueryFromModel,
Chris@489 308 QString
Chris@489 309 (
Chris@489 310 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@489 311
Chris@494 312 " SELECT ?output_id "
Chris@489 313
Chris@489 314 " WHERE { "
Chris@489 315 " <%1> vamp:output ?output . "
Chris@494 316 " ?output vamp:identifier ?output_id "
Chris@489 317 " } "
Chris@489 318 )
Chris@489 319 .arg(transformUri),
Chris@494 320 "output_id");
Chris@489 321
Chris@489 322 if (outputValue.type == SimpleSPARQLQuery::NoValue) {
Chris@489 323 return true;
Chris@489 324 }
Chris@494 325
Chris@494 326 if (outputValue.type != SimpleSPARQLQuery::LiteralValue) {
Chris@494 327 m_errorString = QString("No vamp:identifier found for output of transform <%1>, or vamp:identifier is not a literal").arg(transformUri);
Chris@489 328 return false;
Chris@489 329 }
Chris@489 330
Chris@494 331 transform.setOutput(outputValue.value);
Chris@440 332
Chris@440 333 return true;
Chris@440 334 }
Chris@440 335
Chris@440 336
Chris@440 337 bool
Chris@440 338 RDFTransformFactoryImpl::setParameters(Transform &transform,
Chris@489 339 QString transformUri)
Chris@440 340 {
Chris@440 341 SimpleSPARQLQuery paramQuery
Chris@489 342 (SimpleSPARQLQuery::QueryFromModel,
Chris@480 343 QString
Chris@440 344 (
Chris@440 345 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@440 346
Chris@440 347 " SELECT ?param_id ?param_value "
Chris@440 348
Chris@440 349 " WHERE { "
Chris@489 350 " <%1> vamp:parameter_binding ?binding . "
Chris@440 351 " ?binding vamp:parameter ?param ; "
Chris@440 352 " vamp:value ?param_value . "
Chris@440 353 " ?param vamp:identifier ?param_id "
Chris@440 354 " } "
Chris@440 355 )
Chris@440 356 .arg(transformUri));
Chris@440 357
Chris@440 358 SimpleSPARQLQuery::ResultList paramResults = paramQuery.execute();
Chris@440 359
Chris@440 360 if (!paramQuery.isOK()) {
Chris@440 361 m_errorString = paramQuery.getErrorString();
Chris@440 362 return false;
Chris@440 363 }
Chris@440 364
Chris@440 365 if (paramQuery.wasCancelled()) {
Chris@440 366 m_errorString = "Query cancelled";
Chris@440 367 return false;
Chris@440 368 }
Chris@440 369
Chris@440 370 for (int j = 0; j < paramResults.size(); ++j) {
Chris@440 371
Chris@440 372 QString paramId = paramResults[j]["param_id"].value;
Chris@440 373 QString paramValue = paramResults[j]["param_value"].value;
Chris@440 374
Chris@440 375 if (paramId == "" || paramValue == "") continue;
Chris@440 376
Chris@440 377 transform.setParameter(paramId, paramValue.toFloat());
Chris@440 378 }
Chris@440 379
Chris@440 380 return true;
Chris@440 381 }
Chris@440 382
Chris@494 383 QString
Chris@494 384 RDFTransformFactoryImpl::writeTransformToRDF(const Transform &transform,
Chris@494 385 QString uri)
Chris@494 386 {
Chris@494 387 QString str;
Chris@494 388 QTextStream s(&str);
Chris@494 389
Chris@503 390 // assumes the usual prefixes are available; requires that uri be
Chris@503 391 // a local fragment (e.g. ":transform") rather than a uri enclosed
Chris@503 392 // in <>, so that we can suffix it if need be
Chris@494 393
Chris@494 394 QString pluginId = transform.getPluginIdentifier();
Chris@494 395 QString pluginUri = PluginRDFIndexer::getInstance()->getURIForPluginId(pluginId);
Chris@494 396
Chris@503 397 if (pluginUri != "") {
Chris@503 398 s << uri << " a vamp:Transform ;" << endl;
Chris@592 399 s << " vamp:plugin <" << QUrl(pluginUri).toEncoded().data() << "> ;" << endl;
Chris@503 400 } else {
Chris@503 401 std::cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No plugin URI available for plugin id \"" << pluginId.toStdString() << "\", writing synthetic plugin and library resources" << std::endl;
Chris@503 402 QString type, soname, label;
Chris@503 403 PluginIdentifier::parseIdentifier(pluginId, type, soname, label);
Chris@503 404 s << uri << "_plugin a vamp:Plugin ;" << endl;
Chris@503 405 s << " vamp:identifier \"" << label << "\" .\n" << endl;
Chris@503 406 s << uri << "_library a vamp:PluginLibrary ;" << endl;
Chris@503 407 s << " vamp:identifier \"" << soname << "\" ;" << endl;
Chris@503 408 s << " vamp:available_plugin " << uri << "_plugin .\n" << endl;
Chris@503 409 s << uri << " a vamp:Transform ;" << endl;
Chris@503 410 s << " vamp:plugin " << uri << "_plugin ;" << endl;
Chris@503 411 }
Chris@503 412
Chris@494 413 PluginRDFDescription description(pluginId);
Chris@503 414 QString outputId = transform.getOutput();
Chris@503 415 QString outputUri = description.getOutputUri(outputId);
Chris@494 416
Chris@494 417 if (transform.getOutput() != "" && outputUri == "") {
Chris@503 418 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 419 }
Chris@494 420
Chris@494 421 if (transform.getStepSize() != 0) {
Chris@494 422 s << " vamp:step_size \"" << transform.getStepSize() << "\"^^xsd:int ; " << endl;
Chris@494 423 }
Chris@494 424 if (transform.getBlockSize() != 0) {
Chris@494 425 s << " vamp:block_size \"" << transform.getBlockSize() << "\"^^xsd:int ; " << endl;
Chris@494 426 }
Chris@494 427 if (transform.getStartTime() != RealTime::zeroTime) {
Chris@494 428 s << " vamp:start \"" << transform.getStartTime().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 429 }
Chris@494 430 if (transform.getDuration() != RealTime::zeroTime) {
Chris@494 431 s << " vamp:duration \"" << transform.getDuration().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 432 }
Chris@494 433 if (transform.getSampleRate() != 0) {
Chris@494 434 s << " vamp:sample_rate \"" << transform.getSampleRate() << "\"^^xsd:float ; " << endl;
Chris@494 435 }
Chris@494 436
Chris@494 437 QString program = transform.getProgram();
Chris@494 438 if (program != "") {
Chris@494 439 s << " vamp:program \"\"\"" << program << "\"\"\" ;" << endl;
Chris@494 440 }
Chris@494 441
Chris@508 442 QString summary = transform.summaryTypeToString(transform.getSummaryType());
Chris@508 443 if (summary != "") {
Chris@508 444 s << " vamp:summary_type \"" << summary << "\" ;" << endl;
Chris@508 445 }
Chris@508 446
Chris@494 447 Transform::ParameterMap parameters = transform.getParameters();
Chris@494 448 for (Transform::ParameterMap::const_iterator i = parameters.begin();
Chris@494 449 i != parameters.end(); ++i) {
Chris@494 450 QString name = i->first;
Chris@494 451 float value = i->second;
Chris@494 452 s << " vamp:parameter_binding [" << endl;
Chris@494 453 s << " vamp:parameter [ vamp:identifier \"" << name << "\" ] ;" << endl;
Chris@494 454 s << " vamp:value \"" << value << "\"^^xsd:float ;" << endl;
Chris@494 455 s << " ] ;" << endl;
Chris@494 456 }
Chris@494 457
Chris@494 458 if (outputUri != "") {
Chris@592 459 s << " vamp:output <" << QUrl(outputUri).toEncoded().data() << "> ." << endl;
Chris@503 460 } else if (outputId != "") {
Chris@503 461 s << " vamp:output [ vamp:identifier \"" << outputId << "\" ] ." << endl;
Chris@494 462 } else {
Chris@494 463 s << " ." << endl;
Chris@494 464 }
Chris@494 465
Chris@494 466 return str;
Chris@494 467 }
Chris@494 468