annotate rdf/RDFTransformFactory.cpp @ 1078:ce82bcdc95d0

Fail upfront if the file is going to be too large. We expect the caller to split up large data sets into several MatrixFiles
author Chris Cannam
date Wed, 10 Jun 2015 13:10:26 +0100
parents 7d003fe48225
children d094598f84bd
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@727 7 This file copyright 2008-2012 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 "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@727 34 #include <dataquay/BasicStore.h>
Chris@727 35 #include <dataquay/PropertyObject.h>
Chris@727 36
Chris@727 37 using Dataquay::Uri;
Chris@727 38 using Dataquay::Node;
Chris@727 39 using Dataquay::Nodes;
Chris@727 40 using Dataquay::Triple;
Chris@727 41 using Dataquay::Triples;
Chris@727 42 using Dataquay::BasicStore;
Chris@727 43 using Dataquay::PropertyObject;
Chris@439 44
Chris@439 45
Chris@439 46 class RDFTransformFactoryImpl
Chris@439 47 {
Chris@439 48 public:
Chris@439 49 RDFTransformFactoryImpl(QString url);
Chris@439 50 virtual ~RDFTransformFactoryImpl();
Chris@439 51
Chris@493 52 bool isRDF();
Chris@439 53 bool isOK();
Chris@439 54 QString getErrorString() const;
Chris@439 55
Chris@439 56 std::vector<Transform> getTransforms(ProgressReporter *);
Chris@439 57
Chris@494 58 static QString writeTransformToRDF(const Transform &, QString);
Chris@494 59
Chris@439 60 protected:
Chris@727 61 BasicStore *m_store;
Chris@439 62 QString m_urlString;
Chris@439 63 QString m_errorString;
Chris@493 64 bool m_isRDF;
Chris@489 65 bool setOutput(Transform &, QString);
Chris@489 66 bool setParameters(Transform &, QString);
Chris@439 67 };
Chris@439 68
Chris@439 69
Chris@439 70 QString
Chris@439 71 RDFTransformFactory::getKnownExtensions()
Chris@439 72 {
Chris@439 73 return "*.rdf *.n3 *.ttl";
Chris@439 74 }
Chris@439 75
Chris@439 76 RDFTransformFactory::RDFTransformFactory(QString url) :
Chris@439 77 m_d(new RDFTransformFactoryImpl(url))
Chris@439 78 {
Chris@439 79 }
Chris@439 80
Chris@439 81 RDFTransformFactory::~RDFTransformFactory()
Chris@439 82 {
Chris@439 83 delete m_d;
Chris@439 84 }
Chris@439 85
Chris@439 86 bool
Chris@493 87 RDFTransformFactory::isRDF()
Chris@493 88 {
Chris@493 89 return m_d->isRDF();
Chris@493 90 }
Chris@493 91
Chris@493 92 bool
Chris@439 93 RDFTransformFactory::isOK()
Chris@439 94 {
Chris@439 95 return m_d->isOK();
Chris@439 96 }
Chris@439 97
Chris@439 98 QString
Chris@439 99 RDFTransformFactory::getErrorString() const
Chris@439 100 {
Chris@439 101 return m_d->getErrorString();
Chris@439 102 }
Chris@439 103
Chris@439 104 std::vector<Transform>
Chris@439 105 RDFTransformFactory::getTransforms(ProgressReporter *r)
Chris@439 106 {
Chris@439 107 return m_d->getTransforms(r);
Chris@439 108 }
Chris@439 109
Chris@494 110 QString
Chris@494 111 RDFTransformFactory::writeTransformToRDF(const Transform &t, QString f)
Chris@494 112 {
Chris@494 113 return RDFTransformFactoryImpl::writeTransformToRDF(t, f);
Chris@494 114 }
Chris@494 115
Chris@439 116 RDFTransformFactoryImpl::RDFTransformFactoryImpl(QString url) :
Chris@727 117 m_store(new BasicStore),
Chris@493 118 m_urlString(url),
Chris@493 119 m_isRDF(false)
Chris@439 120 {
Chris@727 121 //!!! retrieve data if remote... then
Chris@727 122 m_store->addPrefix("vamp", Uri("http://purl.org/ontology/vamp/"));
Chris@727 123 try {
Chris@730 124 QUrl qurl;
Chris@730 125 if (url.startsWith("file:")) {
Chris@730 126 qurl = QUrl(url);
Chris@730 127 } else {
Chris@730 128 qurl = QUrl::fromLocalFile(url);
Chris@730 129 }
Chris@730 130 m_store->import(qurl, BasicStore::ImportIgnoreDuplicates);
Chris@727 131 m_isRDF = true;
Chris@727 132 } catch (...) { }
Chris@439 133 }
Chris@439 134
Chris@439 135 RDFTransformFactoryImpl::~RDFTransformFactoryImpl()
Chris@439 136 {
Chris@727 137 delete m_store;
Chris@439 138 }
Chris@439 139
Chris@439 140 bool
Chris@493 141 RDFTransformFactoryImpl::isRDF()
Chris@493 142 {
Chris@493 143 return m_isRDF;
Chris@493 144 }
Chris@493 145
Chris@493 146 bool
Chris@439 147 RDFTransformFactoryImpl::isOK()
Chris@439 148 {
Chris@439 149 return (m_errorString == "");
Chris@439 150 }
Chris@439 151
Chris@439 152 QString
Chris@439 153 RDFTransformFactoryImpl::getErrorString() const
Chris@439 154 {
Chris@439 155 return m_errorString;
Chris@439 156 }
Chris@439 157
Chris@439 158 std::vector<Transform>
Chris@930 159 RDFTransformFactoryImpl::getTransforms(ProgressReporter *)
Chris@439 160 {
Chris@439 161 std::vector<Transform> transforms;
Chris@439 162
Chris@440 163 std::map<QString, Transform> uriTransformMap;
Chris@439 164
Chris@727 165 Nodes tnodes = m_store->match
Chris@730 166 (Triple(Node(), Uri("a"), m_store->expand("vamp:Transform"))).subjects();
Chris@439 167
Chris@728 168 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
Chris@728 169
Chris@727 170 foreach (Node tnode, tnodes) {
Chris@439 171
Chris@730 172 Node pnode = m_store->complete
Chris@730 173 (Triple(tnode, m_store->expand("vamp:plugin"), Node()));
Chris@440 174
Chris@728 175 if (pnode == Node()) {
Chris@728 176 cerr << "RDFTransformFactory: WARNING: No vamp:plugin for "
Chris@728 177 << "vamp:Transform node " << tnode
Chris@728 178 << ", skipping this transform" << endl;
Chris@728 179 continue;
Chris@728 180 }
Chris@440 181
Chris@728 182 QString transformUri = tnode.value;
Chris@728 183 QString pluginUri = pnode.value;
Chris@439 184
Chris@439 185 QString pluginId = indexer->getIdForPluginURI(pluginUri);
Chris@439 186 if (pluginId == "") {
Chris@439 187 cerr << "RDFTransformFactory: WARNING: Unknown plugin <"
Chris@686 188 << pluginUri << "> for transform <"
Chris@686 189 << transformUri << ">, skipping this transform"
Chris@440 190 << endl;
Chris@440 191 continue;
Chris@440 192 }
Chris@440 193
Chris@439 194 Transform transform;
Chris@439 195 transform.setPluginIdentifier(pluginId);
Chris@439 196
Chris@489 197 if (!setOutput(transform, transformUri)) {
Chris@439 198 return transforms;
Chris@439 199 }
Chris@439 200
Chris@489 201 if (!setParameters(transform, transformUri)) {
Chris@439 202 return transforms;
Chris@439 203 }
Chris@439 204
Chris@440 205 uriTransformMap[transformUri] = transform;
Chris@439 206
Chris@489 207 static const char *optionals[] = {
Chris@489 208 "program",
Chris@508 209 "summary_type",
Chris@489 210 "step_size",
Chris@489 211 "block_size",
Chris@489 212 "window_type",
Chris@489 213 "sample_rate",
Chris@489 214 "start",
Chris@994 215 "duration",
Chris@994 216 "plugin_version"
Chris@489 217 };
Chris@489 218
Chris@930 219 for (int j = 0; j < int(sizeof(optionals)/sizeof(optionals[0])); ++j) {
Chris@439 220
Chris@489 221 QString optional = optionals[j];
Chris@489 222
Chris@730 223 Node onode = m_store->complete
Chris@730 224 (Triple(Uri(transformUri),
Chris@730 225 m_store->expand(QString("vamp:") + optional), Node()));
Chris@440 226
Chris@728 227 if (onode.type != Node::Literal) continue;
Chris@440 228
Chris@728 229 if (optional == "program") {
Chris@728 230 transform.setProgram(onode.value);
Chris@728 231 } else if (optional == "summary_type") {
Chris@728 232 transform.setSummaryType
Chris@728 233 (transform.stringToSummaryType(onode.value));
Chris@728 234 } else if (optional == "step_size") {
Chris@728 235 transform.setStepSize(onode.value.toUInt());
Chris@728 236 } else if (optional == "block_size") {
Chris@728 237 transform.setBlockSize(onode.value.toUInt());
Chris@728 238 } else if (optional == "window_type") {
Chris@728 239 transform.setWindowType
Chris@728 240 (Window<float>::getTypeForName
Chris@728 241 (onode.value.toLower().toStdString()));
Chris@728 242 } else if (optional == "sample_rate") {
Chris@728 243 transform.setSampleRate(onode.value.toFloat());
Chris@728 244 } else if (optional == "start") {
Chris@987 245 RealTime start = RealTime::fromXsdDuration(onode.value.toStdString());
Chris@987 246 transform.setStartTime(start);
Chris@728 247 } else if (optional == "duration") {
Chris@987 248 RealTime duration = RealTime::fromXsdDuration(onode.value.toStdString());
Chris@987 249 transform.setDuration(duration);
Chris@987 250 if (duration == RealTime::zeroTime) {
Chris@987 251 cerr << "\nRDFTransformFactory: WARNING: Duration is specified as \"" << onode.value << "\" in RDF file,\n but this evaluates to zero when parsed as an xsd:duration datatype.\n The duration property will therefore be ignored.\n To specify start time and duration use the xsd:duration format,\n for example \"PT2.5S\"^^xsd:duration (for 2.5 seconds).\n\n";
Chris@987 252 }
Chris@994 253 } else if (optional == "plugin_version") {
Chris@994 254 transform.setPluginVersion(onode.value);
Chris@728 255 } else {
Chris@728 256 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional << "\"" << endl;
Chris@440 257 }
Chris@440 258 }
Chris@440 259
Chris@730 260 cerr << "RDFTransformFactory: NOTE: Transform is: " << endl;
Chris@686 261 cerr << transform.toXmlString() << endl;
Chris@439 262
Chris@439 263 transforms.push_back(transform);
Chris@439 264 }
Chris@439 265
Chris@439 266 return transforms;
Chris@439 267 }
Chris@439 268
Chris@440 269 bool
Chris@440 270 RDFTransformFactoryImpl::setOutput(Transform &transform,
Chris@489 271 QString transformUri)
Chris@440 272 {
Chris@730 273 Node outputNode = m_store->complete
Chris@730 274 (Triple(Uri(transformUri), m_store->expand("vamp:output"), Node()));
Chris@728 275
Chris@728 276 if (outputNode == Node()) return true;
Chris@489 277
Chris@728 278 if (outputNode.type != Node::URI && outputNode.type != Node::Blank) {
Chris@728 279 m_errorString = QString("vamp:output for output of transform <%1> is not a URI or blank node").arg(transformUri);
Chris@728 280 return false;
Chris@489 281 }
Chris@728 282
Chris@728 283 // Now, outputNode might be the subject of a triple within m_store
Chris@728 284 // that tells us the vamp:identifier, or it might be the subject
Chris@728 285 // of a triple within the indexer that tells us it
Chris@728 286
Chris@730 287 Node identNode = m_store->complete
Chris@730 288 (Triple(outputNode, m_store->expand("vamp:identifier"), Node()));
Chris@728 289
Chris@728 290 if (identNode == Node()) {
Chris@728 291 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
Chris@728 292 const BasicStore *index = indexer->getIndex();
Chris@730 293 identNode = index->complete
Chris@730 294 (Triple(outputNode, index->expand("vamp:identifier"), Node()));
Chris@728 295 }
Chris@728 296
Chris@728 297 if (identNode == Node() || identNode.type != Node::Literal) {
Chris@494 298 m_errorString = QString("No vamp:identifier found for output of transform <%1>, or vamp:identifier is not a literal").arg(transformUri);
Chris@489 299 return false;
Chris@489 300 }
Chris@489 301
Chris@728 302 transform.setOutput(identNode.value);
Chris@440 303
Chris@440 304 return true;
Chris@440 305 }
Chris@440 306
Chris@440 307
Chris@440 308 bool
Chris@440 309 RDFTransformFactoryImpl::setParameters(Transform &transform,
Chris@489 310 QString transformUri)
Chris@440 311 {
Chris@729 312 Nodes bindings = m_store->match
Chris@730 313 (Triple(Uri(transformUri), m_store->expand("vamp:parameter_binding"), Node())).objects();
Chris@440 314
Chris@729 315 foreach (Node binding, bindings) {
Chris@729 316
Chris@730 317 Node paramNode = m_store->complete
Chris@730 318 (Triple(binding, m_store->expand("vamp:parameter"), Node()));
Chris@729 319
Chris@729 320 if (paramNode == Node()) {
Chris@729 321 cerr << "RDFTransformFactoryImpl::setParameters: No vamp:parameter for binding " << binding << endl;
Chris@729 322 continue;
Chris@729 323 }
Chris@729 324
Chris@730 325 Node valueNode = m_store->complete
Chris@730 326 (Triple(binding, m_store->expand("vamp:value"), Node()));
Chris@729 327
Chris@729 328 if (paramNode == Node()) {
Chris@729 329 cerr << "RDFTransformFactoryImpl::setParameters: No vamp:value for binding " << binding << endl;
Chris@729 330 continue;
Chris@729 331 }
Chris@730 332
Chris@730 333 // As with output above, paramNode might be the subject of a
Chris@730 334 // triple within m_store that tells us the vamp:identifier, or
Chris@730 335 // it might be the subject of a triple within the indexer that
Chris@730 336 // tells us it
Chris@729 337
Chris@730 338 Node idNode = m_store->complete
Chris@730 339 (Triple(paramNode, m_store->expand("vamp:identifier"), Node()));
Chris@730 340
Chris@729 341 if (idNode == Node()) {
Chris@730 342 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
Chris@730 343 const BasicStore *index = indexer->getIndex();
Chris@730 344 idNode = index->complete
Chris@730 345 (Triple(paramNode, index->expand("vamp:identifier"), Node()));
Chris@730 346 }
Chris@730 347
Chris@730 348 if (idNode == Node() || idNode.type != Node::Literal) {
Chris@729 349 cerr << "RDFTransformFactoryImpl::setParameters: No vamp:identifier for parameter " << paramNode << endl;
Chris@729 350 continue;
Chris@729 351 }
Chris@440 352
Chris@729 353 transform.setParameter(idNode.value, valueNode.value.toFloat());
Chris@440 354 }
Chris@440 355
Chris@440 356 return true;
Chris@440 357 }
Chris@440 358
Chris@494 359 QString
Chris@494 360 RDFTransformFactoryImpl::writeTransformToRDF(const Transform &transform,
Chris@494 361 QString uri)
Chris@494 362 {
Chris@494 363 QString str;
Chris@494 364 QTextStream s(&str);
Chris@494 365
Chris@503 366 // assumes the usual prefixes are available; requires that uri be
Chris@503 367 // a local fragment (e.g. ":transform") rather than a uri enclosed
Chris@503 368 // in <>, so that we can suffix it if need be
Chris@494 369
Chris@494 370 QString pluginId = transform.getPluginIdentifier();
Chris@494 371 QString pluginUri = PluginRDFIndexer::getInstance()->getURIForPluginId(pluginId);
Chris@494 372
Chris@503 373 if (pluginUri != "") {
Chris@503 374 s << uri << " a vamp:Transform ;" << endl;
Chris@592 375 s << " vamp:plugin <" << QUrl(pluginUri).toEncoded().data() << "> ;" << endl;
Chris@503 376 } else {
Chris@843 377 cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No plugin URI available for plugin id \"" << pluginId << "\", writing synthetic plugin and library resources" << endl;
Chris@503 378 QString type, soname, label;
Chris@503 379 PluginIdentifier::parseIdentifier(pluginId, type, soname, label);
Chris@503 380 s << uri << "_plugin a vamp:Plugin ;" << endl;
Chris@503 381 s << " vamp:identifier \"" << label << "\" .\n" << endl;
Chris@503 382 s << uri << "_library a vamp:PluginLibrary ;" << endl;
Chris@503 383 s << " vamp:identifier \"" << soname << "\" ;" << endl;
Chris@503 384 s << " vamp:available_plugin " << uri << "_plugin .\n" << endl;
Chris@503 385 s << uri << " a vamp:Transform ;" << endl;
Chris@503 386 s << " vamp:plugin " << uri << "_plugin ;" << endl;
Chris@503 387 }
Chris@503 388
Chris@494 389 PluginRDFDescription description(pluginId);
Chris@503 390 QString outputId = transform.getOutput();
Chris@503 391 QString outputUri = description.getOutputUri(outputId);
Chris@494 392
Chris@494 393 if (transform.getOutput() != "" && outputUri == "") {
Chris@843 394 cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No output URI available for transform output id \"" << transform.getOutput() << "\", writing a synthetic output resource" << endl;
Chris@494 395 }
Chris@494 396
Chris@494 397 if (transform.getStepSize() != 0) {
Chris@494 398 s << " vamp:step_size \"" << transform.getStepSize() << "\"^^xsd:int ; " << endl;
Chris@494 399 }
Chris@494 400 if (transform.getBlockSize() != 0) {
Chris@494 401 s << " vamp:block_size \"" << transform.getBlockSize() << "\"^^xsd:int ; " << endl;
Chris@494 402 }
Chris@1004 403 if (transform.getWindowType() != HanningWindow) {
Chris@1004 404 s << " vamp:window_type \"" <<
Chris@1004 405 Window<float>::getNameForType(transform.getWindowType()).c_str()
Chris@1004 406 << "\" ; " << endl;
Chris@1004 407 }
Chris@494 408 if (transform.getStartTime() != RealTime::zeroTime) {
Chris@494 409 s << " vamp:start \"" << transform.getStartTime().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 410 }
Chris@494 411 if (transform.getDuration() != RealTime::zeroTime) {
Chris@494 412 s << " vamp:duration \"" << transform.getDuration().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 413 }
Chris@494 414 if (transform.getSampleRate() != 0) {
Chris@494 415 s << " vamp:sample_rate \"" << transform.getSampleRate() << "\"^^xsd:float ; " << endl;
Chris@494 416 }
Chris@1003 417 if (transform.getPluginVersion() != "") {
Chris@1003 418 s << " vamp:plugin_version \"\"\"" << transform.getPluginVersion() << "\"\"\" ; " << endl;
Chris@1003 419 }
Chris@494 420
Chris@494 421 QString program = transform.getProgram();
Chris@494 422 if (program != "") {
Chris@494 423 s << " vamp:program \"\"\"" << program << "\"\"\" ;" << endl;
Chris@494 424 }
Chris@494 425
Chris@508 426 QString summary = transform.summaryTypeToString(transform.getSummaryType());
Chris@508 427 if (summary != "") {
Chris@508 428 s << " vamp:summary_type \"" << summary << "\" ;" << endl;
Chris@508 429 }
Chris@508 430
Chris@494 431 Transform::ParameterMap parameters = transform.getParameters();
Chris@494 432 for (Transform::ParameterMap::const_iterator i = parameters.begin();
Chris@494 433 i != parameters.end(); ++i) {
Chris@494 434 QString name = i->first;
Chris@494 435 float value = i->second;
Chris@494 436 s << " vamp:parameter_binding [" << endl;
Chris@494 437 s << " vamp:parameter [ vamp:identifier \"" << name << "\" ] ;" << endl;
Chris@494 438 s << " vamp:value \"" << value << "\"^^xsd:float ;" << endl;
Chris@494 439 s << " ] ;" << endl;
Chris@494 440 }
Chris@494 441
Chris@494 442 if (outputUri != "") {
Chris@592 443 s << " vamp:output <" << QUrl(outputUri).toEncoded().data() << "> ." << endl;
Chris@503 444 } else if (outputId != "") {
Chris@503 445 s << " vamp:output [ vamp:identifier \"" << outputId << "\" ] ." << endl;
Chris@494 446 } else {
Chris@494 447 s << " ." << endl;
Chris@494 448 }
Chris@494 449
Chris@494 450 return str;
Chris@494 451 }
Chris@494 452