annotate rdf/PluginRDFIndexer.cpp @ 489:82ab61fa9223

* Reorganise our sparql queries on the basis that Redland must be available, not only optional. So for anything querying the pool of data about plugins, we use a single datastore and model which is initialised at the outset by PluginRDFIndexer and then queried directly; for anything that "reads from a file" (e.g. loading annotations) we query directly using Rasqal, going to the datastore when we need additional plugin-related information. This may improve performance, but mostly it simplifies the code and fixes a serious issue with RDF import in the previous versions (namely that multiple sequential RDF imports would end up sharing the same RDF data pool!)
author Chris Cannam
date Fri, 21 Nov 2008 16:12:29 +0000
parents b13213785a6f
children 1b8c748fd7ea
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 "PluginRDFIndexer.h"
Chris@439 17
Chris@439 18 #include "SimpleSPARQLQuery.h"
Chris@439 19
Chris@467 20 #include "data/fileio/CachedFile.h"
Chris@471 21 #include "data/fileio/FileSource.h"
Chris@461 22 #include "data/fileio/PlaylistFileReader.h"
Chris@439 23 #include "plugin/PluginIdentifier.h"
Chris@439 24
Chris@457 25 #include "base/Profiler.h"
Chris@457 26
Chris@475 27 #include <vamp-hostsdk/PluginHostAdapter.h>
Chris@439 28
Chris@439 29 #include <QFileInfo>
Chris@439 30 #include <QDir>
Chris@439 31 #include <QUrl>
Chris@461 32 #include <QDateTime>
Chris@461 33 #include <QSettings>
Chris@461 34 #include <QFile>
Chris@439 35
Chris@439 36 #include <iostream>
Chris@439 37 using std::cerr;
Chris@439 38 using std::endl;
Chris@439 39 using std::vector;
Chris@439 40 using std::string;
Chris@439 41 using Vamp::PluginHostAdapter;
Chris@439 42
Chris@439 43 PluginRDFIndexer *
Chris@439 44 PluginRDFIndexer::m_instance = 0;
Chris@439 45
Chris@439 46 PluginRDFIndexer *
Chris@439 47 PluginRDFIndexer::getInstance()
Chris@439 48 {
Chris@439 49 if (!m_instance) m_instance = new PluginRDFIndexer();
Chris@439 50 return m_instance;
Chris@439 51 }
Chris@439 52
Chris@439 53 PluginRDFIndexer::PluginRDFIndexer()
Chris@439 54 {
Chris@477 55 indexInstalledURLs();
Chris@477 56 }
Chris@477 57
Chris@477 58 PluginRDFIndexer::~PluginRDFIndexer()
Chris@477 59 {
Chris@477 60 QMutexLocker locker(&m_mutex);
Chris@477 61 }
Chris@477 62
Chris@477 63 void
Chris@477 64 PluginRDFIndexer::indexInstalledURLs()
Chris@477 65 {
Chris@439 66 vector<string> paths = PluginHostAdapter::getPluginPath();
Chris@439 67
Chris@439 68 QStringList filters;
Chris@439 69 filters << "*.n3";
Chris@439 70 filters << "*.N3";
Chris@439 71 filters << "*.rdf";
Chris@439 72 filters << "*.RDF";
Chris@439 73
Chris@439 74 // Search each Vamp plugin path for a .rdf file that either has
Chris@439 75 // name "soname", "soname:label" or "soname/label" plus RDF
Chris@439 76 // extension. Use that order of preference, and prefer n3 over
Chris@439 77 // rdf extension.
Chris@439 78
Chris@439 79 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
Chris@439 80
Chris@439 81 QDir dir(i->c_str());
Chris@439 82 if (!dir.exists()) continue;
Chris@439 83
Chris@439 84 QStringList entries = dir.entryList
Chris@439 85 (filters, QDir::Files | QDir::Readable);
Chris@439 86
Chris@439 87 for (QStringList::const_iterator j = entries.begin();
Chris@439 88 j != entries.end(); ++j) {
Chris@439 89 QFileInfo fi(dir.filePath(*j));
Chris@489 90 pullFile(fi.absoluteFilePath());
Chris@439 91 }
Chris@439 92
Chris@439 93 QStringList subdirs = dir.entryList
Chris@439 94 (QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Readable);
Chris@439 95
Chris@439 96 for (QStringList::const_iterator j = subdirs.begin();
Chris@439 97 j != subdirs.end(); ++j) {
Chris@439 98 QDir subdir(dir.filePath(*j));
Chris@439 99 if (subdir.exists()) {
Chris@439 100 entries = subdir.entryList
Chris@439 101 (filters, QDir::Files | QDir::Readable);
Chris@439 102 for (QStringList::const_iterator k = entries.begin();
Chris@439 103 k != entries.end(); ++k) {
Chris@439 104 QFileInfo fi(subdir.filePath(*k));
Chris@489 105 pullFile(fi.absoluteFilePath());
Chris@439 106 }
Chris@439 107 }
Chris@439 108 }
Chris@439 109 }
Chris@489 110
Chris@489 111 reindex();
Chris@439 112 }
Chris@439 113
Chris@461 114 bool
Chris@461 115 PluginRDFIndexer::indexConfiguredURLs()
Chris@461 116 {
Chris@461 117 std::cerr << "PluginRDFIndexer::indexConfiguredURLs" << std::endl;
Chris@461 118
Chris@461 119 QSettings settings;
Chris@461 120 settings.beginGroup("RDF");
Chris@461 121
Chris@461 122 QString indexKey("rdf-indices");
Chris@461 123 QStringList indices = settings.value(indexKey).toStringList();
Chris@461 124
Chris@461 125 for (int i = 0; i < indices.size(); ++i) {
Chris@461 126
Chris@461 127 QString index = indices[i];
Chris@461 128
Chris@461 129 std::cerr << "PluginRDFIndexer::indexConfiguredURLs: index url is "
Chris@461 130 << index.toStdString() << std::endl;
Chris@461 131
Chris@467 132 CachedFile cf(index);
Chris@467 133 if (!cf.isOK()) continue;
Chris@467 134
Chris@467 135 FileSource indexSource(cf.getLocalFilename());
Chris@461 136
Chris@461 137 PlaylistFileReader reader(indexSource);
Chris@461 138 if (!reader.isOK()) continue;
Chris@461 139
Chris@461 140 PlaylistFileReader::Playlist list = reader.load();
Chris@461 141 for (PlaylistFileReader::Playlist::const_iterator j = list.begin();
Chris@461 142 j != list.end(); ++j) {
Chris@461 143 std::cerr << "PluginRDFIndexer::indexConfiguredURLs: url is "
Chris@461 144 << j->toStdString() << std::endl;
Chris@489 145 pullURL(*j);
Chris@461 146 }
Chris@461 147 }
Chris@461 148
Chris@461 149 QString urlListKey("rdf-urls");
Chris@461 150 QStringList urls = settings.value(urlListKey).toStringList();
Chris@461 151
Chris@461 152 for (int i = 0; i < urls.size(); ++i) {
Chris@489 153 pullURL(urls[i]);
Chris@461 154 }
Chris@461 155
Chris@461 156 settings.endGroup();
Chris@489 157 reindex();
Chris@461 158 return true;
Chris@461 159 }
Chris@461 160
Chris@439 161 QString
Chris@439 162 PluginRDFIndexer::getURIForPluginId(QString pluginId)
Chris@439 163 {
Chris@461 164 QMutexLocker locker(&m_mutex);
Chris@461 165
Chris@439 166 if (m_idToUriMap.find(pluginId) == m_idToUriMap.end()) return "";
Chris@439 167 return m_idToUriMap[pluginId];
Chris@439 168 }
Chris@439 169
Chris@439 170 QString
Chris@439 171 PluginRDFIndexer::getIdForPluginURI(QString uri)
Chris@439 172 {
Chris@476 173 m_mutex.lock();
Chris@461 174
Chris@439 175 if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
Chris@439 176
Chris@476 177 m_mutex.unlock();
Chris@476 178
Chris@439 179 // Haven't found this uri referenced in any document on the
Chris@439 180 // local filesystem; try resolving the pre-fragment part of
Chris@439 181 // the uri as a document URL and reading that if possible.
Chris@439 182
Chris@439 183 // Because we may want to refer to this document again, we
Chris@439 184 // cache it locally if it turns out to exist.
Chris@439 185
Chris@439 186 cerr << "PluginRDFIndexer::getIdForPluginURI: NOTE: Failed to find a local RDF document describing plugin <" << uri.toStdString() << ">: attempting to retrieve one remotely by guesswork" << endl;
Chris@439 187
Chris@439 188 QString baseUrl = QUrl(uri).toString(QUrl::RemoveFragment);
Chris@439 189
Chris@457 190 indexURL(baseUrl);
Chris@439 191
Chris@476 192 m_mutex.lock();
Chris@476 193
Chris@439 194 if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
Chris@439 195 m_uriToIdMap[uri] = "";
Chris@439 196 }
Chris@439 197 }
Chris@439 198
Chris@476 199 QString id = m_uriToIdMap[uri];
Chris@476 200 m_mutex.unlock();
Chris@476 201 return id;
Chris@439 202 }
Chris@439 203
Chris@456 204 QStringList
Chris@456 205 PluginRDFIndexer::getIndexedPluginIds()
Chris@456 206 {
Chris@461 207 QMutexLocker locker(&m_mutex);
Chris@461 208
Chris@456 209 QStringList ids;
Chris@489 210 for (StringMap::const_iterator i = m_idToUriMap.begin();
Chris@489 211 i != m_idToUriMap.end(); ++i) {
Chris@456 212 ids.push_back(i->first);
Chris@456 213 }
Chris@456 214 return ids;
Chris@456 215 }
Chris@456 216
Chris@439 217 bool
Chris@489 218 PluginRDFIndexer::pullFile(QString filepath)
Chris@439 219 {
Chris@439 220 QUrl url = QUrl::fromLocalFile(filepath);
Chris@439 221 QString urlString = url.toString();
Chris@489 222 return pullURL(urlString);
Chris@439 223 }
Chris@461 224
Chris@439 225 bool
Chris@439 226 PluginRDFIndexer::indexURL(QString urlString)
Chris@439 227 {
Chris@489 228 bool pulled = pullURL(urlString);
Chris@489 229 if (!pulled) return false;
Chris@489 230 reindex();
Chris@489 231 return true;
Chris@489 232 }
Chris@489 233
Chris@489 234 bool
Chris@489 235 PluginRDFIndexer::pullURL(QString urlString)
Chris@489 236 {
Chris@457 237 Profiler profiler("PluginRDFIndexer::indexURL");
Chris@457 238
Chris@461 239 std::cerr << "PluginRDFIndexer::indexURL(" << urlString.toStdString() << ")" << std::endl;
Chris@461 240
Chris@461 241 QMutexLocker locker(&m_mutex);
Chris@461 242
Chris@457 243 QString localString = urlString;
Chris@457 244
Chris@457 245 if (FileSource::isRemote(urlString) &&
Chris@457 246 FileSource::canHandleScheme(urlString)) {
Chris@457 247
Chris@467 248 CachedFile cf(urlString);
Chris@467 249 if (!cf.isOK()) {
Chris@467 250 return false;
Chris@467 251 }
Chris@467 252
Chris@483 253 localString = QUrl::fromLocalFile(cf.getLocalFilename()).toString();
Chris@457 254 }
Chris@457 255
Chris@489 256 return SimpleSPARQLQuery::addSourceToModel(localString);
Chris@489 257 }
Chris@489 258
Chris@489 259 bool
Chris@489 260 PluginRDFIndexer::reindex()
Chris@489 261 {
Chris@489 262 SimpleSPARQLQuery::QueryType m = SimpleSPARQLQuery::QueryFromModel;
Chris@489 263
Chris@439 264 SimpleSPARQLQuery query
Chris@489 265 (m,
Chris@481 266 QString
Chris@481 267 (
Chris@481 268 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@481 269
Chris@481 270 " SELECT ?plugin ?library ?plugin_id "
Chris@481 271
Chris@481 272 " WHERE { "
Chris@481 273 " ?plugin a vamp:Plugin . "
Chris@481 274 " ?plugin vamp:identifier ?plugin_id . "
Chris@481 275
Chris@481 276 " OPTIONAL { "
Chris@481 277 " ?library vamp:available_plugin ?plugin "
Chris@481 278 " } "
Chris@481 279 " } "
Chris@489 280 ));
Chris@439 281
Chris@439 282 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@439 283
Chris@439 284 if (!query.isOK()) {
Chris@489 285 cerr << "ERROR: PluginRDFIndexer::reindex: ERROR: Failed to query plugins from model: "
Chris@439 286 << query.getErrorString().toStdString() << endl;
Chris@439 287 return false;
Chris@439 288 }
Chris@439 289
Chris@439 290 if (results.empty()) {
Chris@489 291 cerr << "PluginRDFIndexer::reindex: NOTE: no vamp:Plugin resources found in indexed documents" << endl;
Chris@439 292 return false;
Chris@439 293 }
Chris@439 294
Chris@439 295 bool foundSomething = false;
Chris@439 296 bool addedSomething = false;
Chris@439 297
Chris@439 298 for (SimpleSPARQLQuery::ResultList::iterator i = results.begin();
Chris@439 299 i != results.end(); ++i) {
Chris@439 300
Chris@439 301 QString pluginUri = (*i)["plugin"].value;
Chris@481 302 QString soUri = (*i)["library"].value;
Chris@439 303 QString identifier = (*i)["plugin_id"].value;
Chris@439 304
Chris@439 305 if (identifier == "") {
Chris@489 306 cerr << "PluginRDFIndexer::reindex: NOTE: No vamp:identifier for plugin <"
Chris@439 307 << pluginUri.toStdString() << ">"
Chris@439 308 << endl;
Chris@439 309 continue;
Chris@439 310 }
Chris@481 311 if (soUri == "") {
Chris@489 312 cerr << "PluginRDFIndexer::reindex: NOTE: No implementation library for plugin <"
Chris@482 313 << pluginUri.toStdString() << ">"
Chris@439 314 << endl;
Chris@439 315 continue;
Chris@439 316 }
Chris@481 317
Chris@481 318 QString sonameQuery =
Chris@481 319 QString(
Chris@481 320 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@481 321 " SELECT ?library_id "
Chris@481 322 " WHERE { "
Chris@489 323 " <%1> vamp:identifier ?library_id "
Chris@481 324 " } "
Chris@481 325 )
Chris@481 326 .arg(soUri);
Chris@481 327
Chris@481 328 SimpleSPARQLQuery::Value sonameValue =
Chris@489 329 SimpleSPARQLQuery::singleResultQuery(m, sonameQuery, "library_id");
Chris@481 330 QString soname = sonameValue.value;
Chris@481 331 if (soname == "") {
Chris@489 332 cerr << "PluginRDFIndexer::reindex: NOTE: No identifier for library <"
Chris@481 333 << soUri.toStdString() << ">"
Chris@481 334 << endl;
Chris@481 335 continue;
Chris@481 336 }
Chris@481 337
Chris@439 338 QString pluginId = PluginIdentifier::createIdentifier
Chris@439 339 ("vamp", soname, identifier);
Chris@439 340
Chris@439 341 foundSomething = true;
Chris@439 342
Chris@489 343 if (m_idToUriMap.find(pluginId) != m_idToUriMap.end()) {
Chris@439 344 continue;
Chris@439 345 }
Chris@439 346
Chris@439 347 m_idToUriMap[pluginId] = pluginUri;
Chris@439 348
Chris@439 349 addedSomething = true;
Chris@439 350
Chris@439 351 if (pluginUri != "") {
Chris@439 352 if (m_uriToIdMap.find(pluginUri) != m_uriToIdMap.end()) {
Chris@489 353 cerr << "PluginRDFIndexer::reindex: WARNING: Found multiple plugins with the same URI:" << endl;
Chris@439 354 cerr << " 1. Plugin id \"" << m_uriToIdMap[pluginUri].toStdString() << "\"" << endl;
Chris@439 355 cerr << " 2. Plugin id \"" << pluginId.toStdString() << "\"" << endl;
Chris@439 356 cerr << "both claim URI <" << pluginUri.toStdString() << ">" << endl;
Chris@439 357 } else {
Chris@439 358 m_uriToIdMap[pluginUri] = pluginId;
Chris@439 359 }
Chris@439 360 }
Chris@439 361 }
Chris@439 362
Chris@439 363 if (!foundSomething) {
Chris@489 364 cerr << "PluginRDFIndexer::reindex: NOTE: Plugins found, but none sufficiently described" << endl;
Chris@439 365 }
Chris@439 366
Chris@439 367 return addedSomething;
Chris@439 368 }
Chris@439 369
Chris@439 370
Chris@439 371