annotate rdf/RDFTransformFactory.cpp @ 1700:c1208b211d8c single-point

Ensure test fails rather than crashing if this reader doesn't get created
author Chris Cannam <cannam@all-day-breakfast.com>
date Fri, 03 May 2019 15:02:09 +0100
parents d094598f84bd
children
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@1163 132 } catch (const std::exception &e) {
Chris@1163 133 // The file is not RDF -- we report this by returning false
Chris@1163 134 // from isRDF (because we have not reached the m_isRDF = true
Chris@1163 135 // line above), but we also set the error string
Chris@1163 136 m_errorString = e.what();
Chris@1163 137 }
Chris@439 138 }
Chris@439 139
Chris@439 140 RDFTransformFactoryImpl::~RDFTransformFactoryImpl()
Chris@439 141 {
Chris@727 142 delete m_store;
Chris@439 143 }
Chris@439 144
Chris@439 145 bool
Chris@493 146 RDFTransformFactoryImpl::isRDF()
Chris@493 147 {
Chris@493 148 return m_isRDF;
Chris@493 149 }
Chris@493 150
Chris@493 151 bool
Chris@439 152 RDFTransformFactoryImpl::isOK()
Chris@439 153 {
Chris@1163 154 return m_isRDF && (m_errorString == "");
Chris@439 155 }
Chris@439 156
Chris@439 157 QString
Chris@439 158 RDFTransformFactoryImpl::getErrorString() const
Chris@439 159 {
Chris@439 160 return m_errorString;
Chris@439 161 }
Chris@439 162
Chris@439 163 std::vector<Transform>
Chris@930 164 RDFTransformFactoryImpl::getTransforms(ProgressReporter *)
Chris@439 165 {
Chris@439 166 std::vector<Transform> transforms;
Chris@439 167
Chris@1163 168 if (!m_isRDF) return transforms;
Chris@1163 169
Chris@440 170 std::map<QString, Transform> uriTransformMap;
Chris@439 171
Chris@727 172 Nodes tnodes = m_store->match
Chris@730 173 (Triple(Node(), Uri("a"), m_store->expand("vamp:Transform"))).subjects();
Chris@439 174
Chris@728 175 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
Chris@728 176
Chris@727 177 foreach (Node tnode, tnodes) {
Chris@439 178
Chris@730 179 Node pnode = m_store->complete
Chris@730 180 (Triple(tnode, m_store->expand("vamp:plugin"), Node()));
Chris@440 181
Chris@728 182 if (pnode == Node()) {
Chris@728 183 cerr << "RDFTransformFactory: WARNING: No vamp:plugin for "
Chris@728 184 << "vamp:Transform node " << tnode
Chris@728 185 << ", skipping this transform" << endl;
Chris@728 186 continue;
Chris@728 187 }
Chris@440 188
Chris@728 189 QString transformUri = tnode.value;
Chris@728 190 QString pluginUri = pnode.value;
Chris@439 191
Chris@439 192 QString pluginId = indexer->getIdForPluginURI(pluginUri);
Chris@439 193 if (pluginId == "") {
Chris@439 194 cerr << "RDFTransformFactory: WARNING: Unknown plugin <"
Chris@686 195 << pluginUri << "> for transform <"
Chris@686 196 << transformUri << ">, skipping this transform"
Chris@440 197 << endl;
Chris@440 198 continue;
Chris@440 199 }
Chris@440 200
Chris@439 201 Transform transform;
Chris@439 202 transform.setPluginIdentifier(pluginId);
Chris@439 203
Chris@489 204 if (!setOutput(transform, transformUri)) {
Chris@439 205 return transforms;
Chris@439 206 }
Chris@439 207
Chris@489 208 if (!setParameters(transform, transformUri)) {
Chris@439 209 return transforms;
Chris@439 210 }
Chris@439 211
Chris@440 212 uriTransformMap[transformUri] = transform;
Chris@439 213
Chris@489 214 static const char *optionals[] = {
Chris@489 215 "program",
Chris@508 216 "summary_type",
Chris@489 217 "step_size",
Chris@489 218 "block_size",
Chris@489 219 "window_type",
Chris@489 220 "sample_rate",
Chris@489 221 "start",
Chris@994 222 "duration",
Chris@994 223 "plugin_version"
Chris@489 224 };
Chris@489 225
Chris@930 226 for (int j = 0; j < int(sizeof(optionals)/sizeof(optionals[0])); ++j) {
Chris@439 227
Chris@489 228 QString optional = optionals[j];
Chris@489 229
Chris@730 230 Node onode = m_store->complete
Chris@730 231 (Triple(Uri(transformUri),
Chris@730 232 m_store->expand(QString("vamp:") + optional), Node()));
Chris@440 233
Chris@728 234 if (onode.type != Node::Literal) continue;
Chris@440 235
Chris@728 236 if (optional == "program") {
Chris@728 237 transform.setProgram(onode.value);
Chris@728 238 } else if (optional == "summary_type") {
Chris@728 239 transform.setSummaryType
Chris@728 240 (transform.stringToSummaryType(onode.value));
Chris@728 241 } else if (optional == "step_size") {
Chris@728 242 transform.setStepSize(onode.value.toUInt());
Chris@728 243 } else if (optional == "block_size") {
Chris@728 244 transform.setBlockSize(onode.value.toUInt());
Chris@728 245 } else if (optional == "window_type") {
Chris@728 246 transform.setWindowType
Chris@728 247 (Window<float>::getTypeForName
Chris@728 248 (onode.value.toLower().toStdString()));
Chris@728 249 } else if (optional == "sample_rate") {
Chris@728 250 transform.setSampleRate(onode.value.toFloat());
Chris@728 251 } else if (optional == "start") {
Chris@987 252 RealTime start = RealTime::fromXsdDuration(onode.value.toStdString());
Chris@987 253 transform.setStartTime(start);
Chris@728 254 } else if (optional == "duration") {
Chris@987 255 RealTime duration = RealTime::fromXsdDuration(onode.value.toStdString());
Chris@987 256 transform.setDuration(duration);
Chris@987 257 if (duration == RealTime::zeroTime) {
Chris@987 258 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 259 }
Chris@994 260 } else if (optional == "plugin_version") {
Chris@994 261 transform.setPluginVersion(onode.value);
Chris@728 262 } else {
Chris@728 263 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional << "\"" << endl;
Chris@440 264 }
Chris@440 265 }
Chris@440 266
Chris@730 267 cerr << "RDFTransformFactory: NOTE: Transform is: " << endl;
Chris@686 268 cerr << transform.toXmlString() << endl;
Chris@439 269
Chris@439 270 transforms.push_back(transform);
Chris@439 271 }
Chris@439 272
Chris@439 273 return transforms;
Chris@439 274 }
Chris@439 275
Chris@440 276 bool
Chris@440 277 RDFTransformFactoryImpl::setOutput(Transform &transform,
Chris@489 278 QString transformUri)
Chris@440 279 {
Chris@730 280 Node outputNode = m_store->complete
Chris@730 281 (Triple(Uri(transformUri), m_store->expand("vamp:output"), Node()));
Chris@728 282
Chris@728 283 if (outputNode == Node()) return true;
Chris@489 284
Chris@728 285 if (outputNode.type != Node::URI && outputNode.type != Node::Blank) {
Chris@728 286 m_errorString = QString("vamp:output for output of transform <%1> is not a URI or blank node").arg(transformUri);
Chris@728 287 return false;
Chris@489 288 }
Chris@728 289
Chris@728 290 // Now, outputNode might be the subject of a triple within m_store
Chris@728 291 // that tells us the vamp:identifier, or it might be the subject
Chris@728 292 // of a triple within the indexer that tells us it
Chris@728 293
Chris@730 294 Node identNode = m_store->complete
Chris@730 295 (Triple(outputNode, m_store->expand("vamp:identifier"), Node()));
Chris@728 296
Chris@728 297 if (identNode == Node()) {
Chris@728 298 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
Chris@728 299 const BasicStore *index = indexer->getIndex();
Chris@730 300 identNode = index->complete
Chris@730 301 (Triple(outputNode, index->expand("vamp:identifier"), Node()));
Chris@728 302 }
Chris@728 303
Chris@728 304 if (identNode == Node() || identNode.type != Node::Literal) {
Chris@494 305 m_errorString = QString("No vamp:identifier found for output of transform <%1>, or vamp:identifier is not a literal").arg(transformUri);
Chris@489 306 return false;
Chris@489 307 }
Chris@489 308
Chris@728 309 transform.setOutput(identNode.value);
Chris@440 310
Chris@440 311 return true;
Chris@440 312 }
Chris@440 313
Chris@440 314
Chris@440 315 bool
Chris@440 316 RDFTransformFactoryImpl::setParameters(Transform &transform,
Chris@489 317 QString transformUri)
Chris@440 318 {
Chris@729 319 Nodes bindings = m_store->match
Chris@730 320 (Triple(Uri(transformUri), m_store->expand("vamp:parameter_binding"), Node())).objects();
Chris@440 321
Chris@729 322 foreach (Node binding, bindings) {
Chris@729 323
Chris@730 324 Node paramNode = m_store->complete
Chris@730 325 (Triple(binding, m_store->expand("vamp:parameter"), Node()));
Chris@729 326
Chris@729 327 if (paramNode == Node()) {
Chris@729 328 cerr << "RDFTransformFactoryImpl::setParameters: No vamp:parameter for binding " << binding << endl;
Chris@729 329 continue;
Chris@729 330 }
Chris@729 331
Chris@730 332 Node valueNode = m_store->complete
Chris@730 333 (Triple(binding, m_store->expand("vamp:value"), Node()));
Chris@729 334
Chris@729 335 if (paramNode == Node()) {
Chris@729 336 cerr << "RDFTransformFactoryImpl::setParameters: No vamp:value for binding " << binding << endl;
Chris@729 337 continue;
Chris@729 338 }
Chris@730 339
Chris@730 340 // As with output above, paramNode might be the subject of a
Chris@730 341 // triple within m_store that tells us the vamp:identifier, or
Chris@730 342 // it might be the subject of a triple within the indexer that
Chris@730 343 // tells us it
Chris@729 344
Chris@730 345 Node idNode = m_store->complete
Chris@730 346 (Triple(paramNode, m_store->expand("vamp:identifier"), Node()));
Chris@730 347
Chris@729 348 if (idNode == Node()) {
Chris@730 349 PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
Chris@730 350 const BasicStore *index = indexer->getIndex();
Chris@730 351 idNode = index->complete
Chris@730 352 (Triple(paramNode, index->expand("vamp:identifier"), Node()));
Chris@730 353 }
Chris@730 354
Chris@730 355 if (idNode == Node() || idNode.type != Node::Literal) {
Chris@729 356 cerr << "RDFTransformFactoryImpl::setParameters: No vamp:identifier for parameter " << paramNode << endl;
Chris@729 357 continue;
Chris@729 358 }
Chris@440 359
Chris@729 360 transform.setParameter(idNode.value, valueNode.value.toFloat());
Chris@440 361 }
Chris@440 362
Chris@440 363 return true;
Chris@440 364 }
Chris@440 365
Chris@494 366 QString
Chris@494 367 RDFTransformFactoryImpl::writeTransformToRDF(const Transform &transform,
Chris@494 368 QString uri)
Chris@494 369 {
Chris@494 370 QString str;
Chris@494 371 QTextStream s(&str);
Chris@494 372
Chris@503 373 // assumes the usual prefixes are available; requires that uri be
Chris@503 374 // a local fragment (e.g. ":transform") rather than a uri enclosed
Chris@503 375 // in <>, so that we can suffix it if need be
Chris@494 376
Chris@494 377 QString pluginId = transform.getPluginIdentifier();
Chris@494 378 QString pluginUri = PluginRDFIndexer::getInstance()->getURIForPluginId(pluginId);
Chris@494 379
Chris@503 380 if (pluginUri != "") {
Chris@503 381 s << uri << " a vamp:Transform ;" << endl;
Chris@592 382 s << " vamp:plugin <" << QUrl(pluginUri).toEncoded().data() << "> ;" << endl;
Chris@503 383 } else {
Chris@843 384 cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No plugin URI available for plugin id \"" << pluginId << "\", writing synthetic plugin and library resources" << endl;
Chris@503 385 QString type, soname, label;
Chris@503 386 PluginIdentifier::parseIdentifier(pluginId, type, soname, label);
Chris@503 387 s << uri << "_plugin a vamp:Plugin ;" << endl;
Chris@503 388 s << " vamp:identifier \"" << label << "\" .\n" << endl;
Chris@503 389 s << uri << "_library a vamp:PluginLibrary ;" << endl;
Chris@503 390 s << " vamp:identifier \"" << soname << "\" ;" << endl;
Chris@503 391 s << " vamp:available_plugin " << uri << "_plugin .\n" << endl;
Chris@503 392 s << uri << " a vamp:Transform ;" << endl;
Chris@503 393 s << " vamp:plugin " << uri << "_plugin ;" << endl;
Chris@503 394 }
Chris@503 395
Chris@494 396 PluginRDFDescription description(pluginId);
Chris@503 397 QString outputId = transform.getOutput();
Chris@503 398 QString outputUri = description.getOutputUri(outputId);
Chris@494 399
Chris@494 400 if (transform.getOutput() != "" && outputUri == "") {
Chris@843 401 cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No output URI available for transform output id \"" << transform.getOutput() << "\", writing a synthetic output resource" << endl;
Chris@494 402 }
Chris@494 403
Chris@494 404 if (transform.getStepSize() != 0) {
Chris@494 405 s << " vamp:step_size \"" << transform.getStepSize() << "\"^^xsd:int ; " << endl;
Chris@494 406 }
Chris@494 407 if (transform.getBlockSize() != 0) {
Chris@494 408 s << " vamp:block_size \"" << transform.getBlockSize() << "\"^^xsd:int ; " << endl;
Chris@494 409 }
Chris@1004 410 if (transform.getWindowType() != HanningWindow) {
Chris@1004 411 s << " vamp:window_type \"" <<
Chris@1004 412 Window<float>::getNameForType(transform.getWindowType()).c_str()
Chris@1004 413 << "\" ; " << endl;
Chris@1004 414 }
Chris@494 415 if (transform.getStartTime() != RealTime::zeroTime) {
Chris@494 416 s << " vamp:start \"" << transform.getStartTime().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 417 }
Chris@494 418 if (transform.getDuration() != RealTime::zeroTime) {
Chris@494 419 s << " vamp:duration \"" << transform.getDuration().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
Chris@494 420 }
Chris@494 421 if (transform.getSampleRate() != 0) {
Chris@494 422 s << " vamp:sample_rate \"" << transform.getSampleRate() << "\"^^xsd:float ; " << endl;
Chris@494 423 }
Chris@1003 424 if (transform.getPluginVersion() != "") {
Chris@1003 425 s << " vamp:plugin_version \"\"\"" << transform.getPluginVersion() << "\"\"\" ; " << endl;
Chris@1003 426 }
Chris@494 427
Chris@494 428 QString program = transform.getProgram();
Chris@494 429 if (program != "") {
Chris@494 430 s << " vamp:program \"\"\"" << program << "\"\"\" ;" << endl;
Chris@494 431 }
Chris@494 432
Chris@508 433 QString summary = transform.summaryTypeToString(transform.getSummaryType());
Chris@508 434 if (summary != "") {
Chris@508 435 s << " vamp:summary_type \"" << summary << "\" ;" << endl;
Chris@508 436 }
Chris@508 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@592 450 s << " vamp:output <" << QUrl(outputUri).toEncoded().data() << "> ." << 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