annotate rdf/RDFTransformFactory.cpp @ 558:1d7ebc05157e

* Some fairly simplistic code to set up layer type properties based on RDF data about feature types (both when running transforms and when importing features from RDF files).
author Chris Cannam
date Thu, 12 Feb 2009 15:26:43 +0000
parents 1b8c748fd7ea
children 18488253a3f4
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@508 223 "summary_type",
Chris@489 224 "step_size",
Chris@489 225 "block_size",
Chris@489 226 "window_type",
Chris@489 227 "sample_rate",
Chris@489 228 "start",
Chris@489 229 "duration"
Chris@489 230 };
Chris@489 231
Chris@489 232 for (int j = 0; j < sizeof(optionals)/sizeof(optionals[0]); ++j) {
Chris@439 233
Chris@489 234 QString optional = optionals[j];
Chris@489 235
Chris@489 236 QString queryTemplate =
Chris@489 237 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@489 238
Chris@489 239 " SELECT ?%1 "
Chris@489 240
Chris@489 241 " WHERE { "
Chris@489 242 " <%2> vamp:%1 ?%1 "
Chris@489 243 " } ";
Chris@489 244
Chris@489 245 SimpleSPARQLQuery query
Chris@489 246 (SimpleSPARQLQuery::QueryFromModel,
Chris@489 247 queryTemplate.arg(optional).arg(transformUri));
Chris@440 248
Chris@489 249 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@440 250
Chris@489 251 if (!query.isOK()) {
Chris@489 252 m_errorString = query.getErrorString();
Chris@489 253 return transforms;
Chris@440 254 }
Chris@440 255
Chris@489 256 if (results.empty()) continue;
Chris@440 257
Chris@489 258 for (int k = 0; k < results.size(); ++k) {
Chris@489 259
Chris@489 260 const SimpleSPARQLQuery::Value &v = results[k][optional];
Chris@489 261
Chris@489 262 if (v.type == SimpleSPARQLQuery::LiteralValue) {
Chris@440 263
Chris@489 264 if (optional == "program") {
Chris@489 265 transform.setProgram(v.value);
Chris@508 266 } else if (optional == "summary_type") {
Chris@508 267 transform.setSummaryType
Chris@508 268 (transform.stringToSummaryType(v.value));
Chris@489 269 } else if (optional == "step_size") {
Chris@489 270 transform.setStepSize(v.value.toUInt());
Chris@489 271 } else if (optional == "block_size") {
Chris@489 272 transform.setBlockSize(v.value.toUInt());
Chris@489 273 } else if (optional == "window_type") {
Chris@489 274 cerr << "NOTE: can't handle window type yet (value is \""
Chris@489 275 << v.value.toStdString() << "\")" << endl;
Chris@489 276 } else if (optional == "sample_rate") {
Chris@489 277 transform.setSampleRate(v.value.toFloat());
Chris@489 278 } else if (optional == "start") {
Chris@489 279 transform.setStartTime
Chris@489 280 (RealTime::fromXsdDuration(v.value.toStdString()));
Chris@489 281 } else if (optional == "duration") {
Chris@489 282 transform.setDuration
Chris@489 283 (RealTime::fromXsdDuration(v.value.toStdString()));
Chris@489 284 } else {
Chris@489 285 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional.toStdString() << "\"" << endl;
Chris@489 286 }
Chris@440 287 }
Chris@440 288 }
Chris@440 289 }
Chris@440 290
Chris@439 291 cerr << "RDFTransformFactory: NOTE: Transform is: " << endl;
Chris@439 292 cerr << transform.toXmlString().toStdString() << endl;
Chris@439 293
Chris@439 294 transforms.push_back(transform);
Chris@439 295 }
Chris@439 296
Chris@439 297 return transforms;
Chris@439 298 }
Chris@439 299
Chris@440 300 bool
Chris@440 301 RDFTransformFactoryImpl::setOutput(Transform &transform,
Chris@489 302 QString transformUri)
Chris@440 303 {
Chris@489 304 SimpleSPARQLQuery::Value outputValue =
Chris@489 305 SimpleSPARQLQuery::singleResultQuery
Chris@489 306 (SimpleSPARQLQuery::QueryFromModel,
Chris@489 307 QString
Chris@489 308 (
Chris@489 309 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@489 310
Chris@494 311 " SELECT ?output_id "
Chris@489 312
Chris@489 313 " WHERE { "
Chris@489 314 " <%1> vamp:output ?output . "
Chris@494 315 " ?output vamp:identifier ?output_id "
Chris@489 316 " } "
Chris@489 317 )
Chris@489 318 .arg(transformUri),
Chris@494 319 "output_id");
Chris@489 320
Chris@489 321 if (outputValue.type == SimpleSPARQLQuery::NoValue) {
Chris@489 322 return true;
Chris@489 323 }
Chris@494 324
Chris@494 325 if (outputValue.type != SimpleSPARQLQuery::LiteralValue) {
Chris@494 326 m_errorString = QString("No vamp:identifier found for output of transform <%1>, or vamp:identifier is not a literal").arg(transformUri);
Chris@489 327 return false;
Chris@489 328 }
Chris@489 329
Chris@494 330 transform.setOutput(outputValue.value);
Chris@440 331
Chris@440 332 return true;
Chris@440 333 }
Chris@440 334
Chris@440 335
Chris@440 336 bool
Chris@440 337 RDFTransformFactoryImpl::setParameters(Transform &transform,
Chris@489 338 QString transformUri)
Chris@440 339 {
Chris@440 340 SimpleSPARQLQuery paramQuery
Chris@489 341 (SimpleSPARQLQuery::QueryFromModel,
Chris@480 342 QString
Chris@440 343 (
Chris@440 344 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@440 345
Chris@440 346 " SELECT ?param_id ?param_value "
Chris@440 347
Chris@440 348 " WHERE { "
Chris@489 349 " <%1> vamp:parameter_binding ?binding . "
Chris@440 350 " ?binding vamp:parameter ?param ; "
Chris@440 351 " vamp:value ?param_value . "
Chris@440 352 " ?param vamp:identifier ?param_id "
Chris@440 353 " } "
Chris@440 354 )
Chris@440 355 .arg(transformUri));
Chris@440 356
Chris@440 357 SimpleSPARQLQuery::ResultList paramResults = paramQuery.execute();
Chris@440 358
Chris@440 359 if (!paramQuery.isOK()) {
Chris@440 360 m_errorString = paramQuery.getErrorString();
Chris@440 361 return false;
Chris@440 362 }
Chris@440 363
Chris@440 364 if (paramQuery.wasCancelled()) {
Chris@440 365 m_errorString = "Query cancelled";
Chris@440 366 return false;
Chris@440 367 }
Chris@440 368
Chris@440 369 for (int j = 0; j < paramResults.size(); ++j) {
Chris@440 370
Chris@440 371 QString paramId = paramResults[j]["param_id"].value;
Chris@440 372 QString paramValue = paramResults[j]["param_value"].value;
Chris@440 373
Chris@440 374 if (paramId == "" || paramValue == "") continue;
Chris@440 375
Chris@440 376 transform.setParameter(paramId, paramValue.toFloat());
Chris@440 377 }
Chris@440 378
Chris@440 379 return true;
Chris@440 380 }
Chris@440 381
Chris@494 382 QString
Chris@494 383 RDFTransformFactoryImpl::writeTransformToRDF(const Transform &transform,
Chris@494 384 QString uri)
Chris@494 385 {
Chris@494 386 QString str;
Chris@494 387 QTextStream s(&str);
Chris@494 388
Chris@503 389 // assumes the usual prefixes are available; requires that uri be
Chris@503 390 // a local fragment (e.g. ":transform") rather than a uri enclosed
Chris@503 391 // in <>, so that we can suffix it if need be
Chris@494 392
Chris@494 393 QString pluginId = transform.getPluginIdentifier();
Chris@494 394 QString pluginUri = PluginRDFIndexer::getInstance()->getURIForPluginId(pluginId);
Chris@494 395
Chris@503 396 if (pluginUri != "") {
Chris@503 397 s << uri << " a vamp:Transform ;" << endl;
Chris@503 398 s << " vamp:plugin <" << pluginUri << "> ;" << endl;
Chris@503 399 } else {
Chris@503 400 std::cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No plugin URI available for plugin id \"" << pluginId.toStdString() << "\", writing synthetic plugin and library resources" << std::endl;
Chris@503 401 QString type, soname, label;
Chris@503 402 PluginIdentifier::parseIdentifier(pluginId, type, soname, label);
Chris@503 403 s << uri << "_plugin a vamp:Plugin ;" << endl;
Chris@503 404 s << " vamp:identifier \"" << label << "\" .\n" << endl;
Chris@503 405 s << uri << "_library a vamp:PluginLibrary ;" << endl;
Chris@503 406 s << " vamp:identifier \"" << soname << "\" ;" << endl;
Chris@503 407 s << " vamp:available_plugin " << uri << "_plugin .\n" << endl;
Chris@503 408 s << uri << " a vamp:Transform ;" << endl;
Chris@503 409 s << " vamp:plugin " << uri << "_plugin ;" << endl;
Chris@503 410 }
Chris@503 411
Chris@494 412 PluginRDFDescription description(pluginId);
Chris@503 413 QString outputId = transform.getOutput();
Chris@503 414 QString outputUri = description.getOutputUri(outputId);
Chris@494 415
Chris@494 416 if (transform.getOutput() != "" && outputUri == "") {
Chris@503 417 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 418 }
Chris@494 419
Chris@494 420 if (transform.getStepSize() != 0) {
Chris@494 421 s << " vamp:step_size \"" << transform.getStepSize() << "\"^^xsd:int ; " << endl;
Chris@494 422 }
Chris@494 423 if (transform.getBlockSize() != 0) {
Chris@494 424 s << " vamp:block_size \"" << transform.getBlockSize() << "\"^^xsd:int ; " << endl;
Chris@494 425 }
Chris@494 426 if (transform.getStartTime() != RealTime::zeroTime) {
Chris@494 427 s << " vamp:start \"" << transform.getStartTime().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 428 }
Chris@494 429 if (transform.getDuration() != RealTime::zeroTime) {
Chris@494 430 s << " vamp:duration \"" << transform.getDuration().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 431 }
Chris@494 432 if (transform.getSampleRate() != 0) {
Chris@494 433 s << " vamp:sample_rate \"" << transform.getSampleRate() << "\"^^xsd:float ; " << endl;
Chris@494 434 }
Chris@494 435
Chris@494 436 QString program = transform.getProgram();
Chris@494 437 if (program != "") {
Chris@494 438 s << " vamp:program \"\"\"" << program << "\"\"\" ;" << endl;
Chris@494 439 }
Chris@494 440
Chris@508 441 QString summary = transform.summaryTypeToString(transform.getSummaryType());
Chris@508 442 if (summary != "") {
Chris@508 443 s << " vamp:summary_type \"" << summary << "\" ;" << endl;
Chris@508 444 }
Chris@508 445
Chris@494 446 Transform::ParameterMap parameters = transform.getParameters();
Chris@494 447 for (Transform::ParameterMap::const_iterator i = parameters.begin();
Chris@494 448 i != parameters.end(); ++i) {
Chris@494 449 QString name = i->first;
Chris@494 450 float value = i->second;
Chris@494 451 s << " vamp:parameter_binding [" << endl;
Chris@494 452 s << " vamp:parameter [ vamp:identifier \"" << name << "\" ] ;" << endl;
Chris@494 453 s << " vamp:value \"" << value << "\"^^xsd:float ;" << endl;
Chris@494 454 s << " ] ;" << endl;
Chris@494 455 }
Chris@494 456
Chris@494 457 if (outputUri != "") {
Chris@494 458 s << " vamp:output <" << outputUri << "> ." << endl;
Chris@503 459 } else if (outputId != "") {
Chris@503 460 s << " vamp:output [ vamp:identifier \"" << outputId << "\" ] ." << endl;
Chris@494 461 } else {
Chris@494 462 s << " ." << endl;
Chris@494 463 }
Chris@494 464
Chris@494 465 return str;
Chris@494 466 }
Chris@494 467