annotate rdf/RDFTransformFactory.cpp @ 494:81963c51b488

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