annotate rdf/RDFTransformFactory.cpp @ 492:23945cdd7161

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