annotate rdf/RDFTransformFactory.cpp @ 493:3931711b5671

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