annotate rdf/RDFTransformFactory.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +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