annotate rdf/PluginRDFIndexer.cpp @ 661:a4faa1840384

* If a FileSource URL won't convert at all in strict mode, try again in tolerant mode (necessary for e.g. filenames with square brackets in them)
author Chris Cannam
date Tue, 19 Oct 2010 21:47:55 +0100
parents e340b2fb9471
children b4a8d8221eaf
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@520 46 bool
Chris@520 47 PluginRDFIndexer::m_prefixesLoaded = false;
Chris@520 48
Chris@439 49 PluginRDFIndexer *
Chris@439 50 PluginRDFIndexer::getInstance()
Chris@439 51 {
Chris@439 52 if (!m_instance) m_instance = new PluginRDFIndexer();
Chris@439 53 return m_instance;
Chris@439 54 }
Chris@439 55
Chris@439 56 PluginRDFIndexer::PluginRDFIndexer()
Chris@439 57 {
Chris@477 58 indexInstalledURLs();
Chris@477 59 }
Chris@477 60
Chris@477 61 PluginRDFIndexer::~PluginRDFIndexer()
Chris@477 62 {
Chris@477 63 QMutexLocker locker(&m_mutex);
Chris@477 64 }
Chris@477 65
Chris@477 66 void
Chris@477 67 PluginRDFIndexer::indexInstalledURLs()
Chris@477 68 {
Chris@439 69 vector<string> paths = PluginHostAdapter::getPluginPath();
Chris@439 70
Chris@439 71 QStringList filters;
Chris@439 72 filters << "*.n3";
Chris@439 73 filters << "*.N3";
Chris@439 74 filters << "*.rdf";
Chris@439 75 filters << "*.RDF";
Chris@439 76
Chris@439 77 // Search each Vamp plugin path for a .rdf file that either has
Chris@439 78 // name "soname", "soname:label" or "soname/label" plus RDF
Chris@439 79 // extension. Use that order of preference, and prefer n3 over
Chris@439 80 // rdf extension.
Chris@439 81
Chris@439 82 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
Chris@439 83
Chris@439 84 QDir dir(i->c_str());
Chris@439 85 if (!dir.exists()) continue;
Chris@439 86
Chris@439 87 QStringList entries = dir.entryList
Chris@439 88 (filters, QDir::Files | QDir::Readable);
Chris@439 89
Chris@439 90 for (QStringList::const_iterator j = entries.begin();
Chris@439 91 j != entries.end(); ++j) {
Chris@439 92 QFileInfo fi(dir.filePath(*j));
Chris@489 93 pullFile(fi.absoluteFilePath());
Chris@439 94 }
Chris@439 95
Chris@439 96 QStringList subdirs = dir.entryList
Chris@439 97 (QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Readable);
Chris@439 98
Chris@439 99 for (QStringList::const_iterator j = subdirs.begin();
Chris@439 100 j != subdirs.end(); ++j) {
Chris@439 101 QDir subdir(dir.filePath(*j));
Chris@439 102 if (subdir.exists()) {
Chris@439 103 entries = subdir.entryList
Chris@439 104 (filters, QDir::Files | QDir::Readable);
Chris@439 105 for (QStringList::const_iterator k = entries.begin();
Chris@439 106 k != entries.end(); ++k) {
Chris@439 107 QFileInfo fi(subdir.filePath(*k));
Chris@489 108 pullFile(fi.absoluteFilePath());
Chris@439 109 }
Chris@439 110 }
Chris@439 111 }
Chris@439 112 }
Chris@489 113
Chris@489 114 reindex();
Chris@439 115 }
Chris@439 116
Chris@461 117 bool
Chris@461 118 PluginRDFIndexer::indexConfiguredURLs()
Chris@461 119 {
Chris@461 120 std::cerr << "PluginRDFIndexer::indexConfiguredURLs" << std::endl;
Chris@461 121
Chris@461 122 QSettings settings;
Chris@461 123 settings.beginGroup("RDF");
Chris@461 124
Chris@461 125 QString indexKey("rdf-indices");
Chris@461 126 QStringList indices = settings.value(indexKey).toStringList();
Chris@461 127
Chris@461 128 for (int i = 0; i < indices.size(); ++i) {
Chris@461 129
Chris@461 130 QString index = indices[i];
Chris@461 131
Chris@461 132 std::cerr << "PluginRDFIndexer::indexConfiguredURLs: index url is "
Chris@461 133 << index.toStdString() << std::endl;
Chris@461 134
Chris@467 135 CachedFile cf(index);
Chris@467 136 if (!cf.isOK()) continue;
Chris@467 137
Chris@467 138 FileSource indexSource(cf.getLocalFilename());
Chris@461 139
Chris@461 140 PlaylistFileReader reader(indexSource);
Chris@461 141 if (!reader.isOK()) continue;
Chris@461 142
Chris@461 143 PlaylistFileReader::Playlist list = reader.load();
Chris@461 144 for (PlaylistFileReader::Playlist::const_iterator j = list.begin();
Chris@461 145 j != list.end(); ++j) {
Chris@461 146 std::cerr << "PluginRDFIndexer::indexConfiguredURLs: url is "
Chris@461 147 << j->toStdString() << std::endl;
Chris@489 148 pullURL(*j);
Chris@461 149 }
Chris@461 150 }
Chris@461 151
Chris@461 152 QString urlListKey("rdf-urls");
Chris@461 153 QStringList urls = settings.value(urlListKey).toStringList();
Chris@461 154
Chris@461 155 for (int i = 0; i < urls.size(); ++i) {
Chris@489 156 pullURL(urls[i]);
Chris@461 157 }
Chris@461 158
Chris@461 159 settings.endGroup();
Chris@489 160 reindex();
Chris@461 161 return true;
Chris@461 162 }
Chris@461 163
Chris@439 164 QString
Chris@439 165 PluginRDFIndexer::getURIForPluginId(QString pluginId)
Chris@439 166 {
Chris@461 167 QMutexLocker locker(&m_mutex);
Chris@461 168
Chris@439 169 if (m_idToUriMap.find(pluginId) == m_idToUriMap.end()) return "";
Chris@439 170 return m_idToUriMap[pluginId];
Chris@439 171 }
Chris@439 172
Chris@439 173 QString
Chris@439 174 PluginRDFIndexer::getIdForPluginURI(QString uri)
Chris@439 175 {
Chris@476 176 m_mutex.lock();
Chris@461 177
Chris@439 178 if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
Chris@439 179
Chris@476 180 m_mutex.unlock();
Chris@476 181
Chris@439 182 // Haven't found this uri referenced in any document on the
Chris@439 183 // local filesystem; try resolving the pre-fragment part of
Chris@439 184 // the uri as a document URL and reading that if possible.
Chris@439 185
Chris@439 186 // Because we may want to refer to this document again, we
Chris@439 187 // cache it locally if it turns out to exist.
Chris@439 188
Chris@439 189 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 190
Chris@439 191 QString baseUrl = QUrl(uri).toString(QUrl::RemoveFragment);
Chris@439 192
Chris@457 193 indexURL(baseUrl);
Chris@439 194
Chris@476 195 m_mutex.lock();
Chris@476 196
Chris@439 197 if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
Chris@439 198 m_uriToIdMap[uri] = "";
Chris@439 199 }
Chris@439 200 }
Chris@439 201
Chris@476 202 QString id = m_uriToIdMap[uri];
Chris@476 203 m_mutex.unlock();
Chris@476 204 return id;
Chris@439 205 }
Chris@439 206
Chris@456 207 QStringList
Chris@456 208 PluginRDFIndexer::getIndexedPluginIds()
Chris@456 209 {
Chris@461 210 QMutexLocker locker(&m_mutex);
Chris@461 211
Chris@456 212 QStringList ids;
Chris@489 213 for (StringMap::const_iterator i = m_idToUriMap.begin();
Chris@489 214 i != m_idToUriMap.end(); ++i) {
Chris@456 215 ids.push_back(i->first);
Chris@456 216 }
Chris@456 217 return ids;
Chris@456 218 }
Chris@456 219
Chris@439 220 bool
Chris@489 221 PluginRDFIndexer::pullFile(QString filepath)
Chris@439 222 {
Chris@439 223 QUrl url = QUrl::fromLocalFile(filepath);
Chris@439 224 QString urlString = url.toString();
Chris@489 225 return pullURL(urlString);
Chris@439 226 }
Chris@461 227
Chris@439 228 bool
Chris@439 229 PluginRDFIndexer::indexURL(QString urlString)
Chris@439 230 {
Chris@489 231 bool pulled = pullURL(urlString);
Chris@489 232 if (!pulled) return false;
Chris@489 233 reindex();
Chris@489 234 return true;
Chris@489 235 }
Chris@489 236
Chris@489 237 bool
Chris@489 238 PluginRDFIndexer::pullURL(QString urlString)
Chris@489 239 {
Chris@457 240 Profiler profiler("PluginRDFIndexer::indexURL");
Chris@457 241
Chris@520 242 loadPrefixes();
Chris@520 243
Chris@508 244 // std::cerr << "PluginRDFIndexer::indexURL(" << urlString.toStdString() << ")" << std::endl;
Chris@461 245
Chris@461 246 QMutexLocker locker(&m_mutex);
Chris@461 247
Chris@457 248 QString localString = urlString;
Chris@457 249
Chris@457 250 if (FileSource::isRemote(urlString) &&
Chris@457 251 FileSource::canHandleScheme(urlString)) {
Chris@457 252
Chris@520 253 CachedFile cf(urlString, 0, "application/rdf+xml");
Chris@467 254 if (!cf.isOK()) {
Chris@467 255 return false;
Chris@467 256 }
Chris@467 257
Chris@483 258 localString = QUrl::fromLocalFile(cf.getLocalFilename()).toString();
Chris@457 259 }
Chris@457 260
Chris@489 261 return SimpleSPARQLQuery::addSourceToModel(localString);
Chris@489 262 }
Chris@489 263
Chris@489 264 bool
Chris@489 265 PluginRDFIndexer::reindex()
Chris@489 266 {
Chris@489 267 SimpleSPARQLQuery::QueryType m = SimpleSPARQLQuery::QueryFromModel;
Chris@489 268
Chris@439 269 SimpleSPARQLQuery query
Chris@489 270 (m,
Chris@481 271 QString
Chris@481 272 (
Chris@481 273 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@481 274
Chris@481 275 " SELECT ?plugin ?library ?plugin_id "
Chris@481 276
Chris@481 277 " WHERE { "
Chris@481 278 " ?plugin a vamp:Plugin . "
Chris@481 279 " ?plugin vamp:identifier ?plugin_id . "
Chris@481 280
Chris@481 281 " OPTIONAL { "
Chris@481 282 " ?library vamp:available_plugin ?plugin "
Chris@481 283 " } "
Chris@481 284 " } "
Chris@489 285 ));
Chris@439 286
Chris@439 287 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@439 288
Chris@439 289 if (!query.isOK()) {
Chris@489 290 cerr << "ERROR: PluginRDFIndexer::reindex: ERROR: Failed to query plugins from model: "
Chris@439 291 << query.getErrorString().toStdString() << endl;
Chris@439 292 return false;
Chris@439 293 }
Chris@439 294
Chris@439 295 if (results.empty()) {
Chris@489 296 cerr << "PluginRDFIndexer::reindex: NOTE: no vamp:Plugin resources found in indexed documents" << endl;
Chris@439 297 return false;
Chris@439 298 }
Chris@439 299
Chris@439 300 bool foundSomething = false;
Chris@439 301 bool addedSomething = false;
Chris@439 302
Chris@439 303 for (SimpleSPARQLQuery::ResultList::iterator i = results.begin();
Chris@439 304 i != results.end(); ++i) {
Chris@439 305
Chris@439 306 QString pluginUri = (*i)["plugin"].value;
Chris@481 307 QString soUri = (*i)["library"].value;
Chris@439 308 QString identifier = (*i)["plugin_id"].value;
Chris@439 309
Chris@439 310 if (identifier == "") {
Chris@489 311 cerr << "PluginRDFIndexer::reindex: NOTE: No vamp:identifier for plugin <"
Chris@439 312 << pluginUri.toStdString() << ">"
Chris@439 313 << endl;
Chris@439 314 continue;
Chris@439 315 }
Chris@481 316 if (soUri == "") {
Chris@489 317 cerr << "PluginRDFIndexer::reindex: NOTE: No implementation library for plugin <"
Chris@482 318 << pluginUri.toStdString() << ">"
Chris@439 319 << endl;
Chris@439 320 continue;
Chris@439 321 }
Chris@481 322
Chris@481 323 QString sonameQuery =
Chris@481 324 QString(
Chris@481 325 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@481 326 " SELECT ?library_id "
Chris@481 327 " WHERE { "
Chris@489 328 " <%1> vamp:identifier ?library_id "
Chris@481 329 " } "
Chris@481 330 )
Chris@481 331 .arg(soUri);
Chris@481 332
Chris@481 333 SimpleSPARQLQuery::Value sonameValue =
Chris@489 334 SimpleSPARQLQuery::singleResultQuery(m, sonameQuery, "library_id");
Chris@481 335 QString soname = sonameValue.value;
Chris@481 336 if (soname == "") {
Chris@489 337 cerr << "PluginRDFIndexer::reindex: NOTE: No identifier for library <"
Chris@481 338 << soUri.toStdString() << ">"
Chris@481 339 << endl;
Chris@481 340 continue;
Chris@481 341 }
Chris@481 342
Chris@439 343 QString pluginId = PluginIdentifier::createIdentifier
Chris@439 344 ("vamp", soname, identifier);
Chris@439 345
Chris@439 346 foundSomething = true;
Chris@439 347
Chris@489 348 if (m_idToUriMap.find(pluginId) != m_idToUriMap.end()) {
Chris@439 349 continue;
Chris@439 350 }
Chris@439 351
Chris@439 352 m_idToUriMap[pluginId] = pluginUri;
Chris@439 353
Chris@439 354 addedSomething = true;
Chris@439 355
Chris@439 356 if (pluginUri != "") {
Chris@439 357 if (m_uriToIdMap.find(pluginUri) != m_uriToIdMap.end()) {
Chris@489 358 cerr << "PluginRDFIndexer::reindex: WARNING: Found multiple plugins with the same URI:" << endl;
Chris@439 359 cerr << " 1. Plugin id \"" << m_uriToIdMap[pluginUri].toStdString() << "\"" << endl;
Chris@439 360 cerr << " 2. Plugin id \"" << pluginId.toStdString() << "\"" << endl;
Chris@439 361 cerr << "both claim URI <" << pluginUri.toStdString() << ">" << endl;
Chris@439 362 } else {
Chris@439 363 m_uriToIdMap[pluginUri] = pluginId;
Chris@439 364 }
Chris@439 365 }
Chris@439 366 }
Chris@439 367
Chris@439 368 if (!foundSomething) {
Chris@489 369 cerr << "PluginRDFIndexer::reindex: NOTE: Plugins found, but none sufficiently described" << endl;
Chris@439 370 }
Chris@439 371
Chris@439 372 return addedSomething;
Chris@439 373 }
Chris@439 374
Chris@520 375 void
Chris@520 376 PluginRDFIndexer::loadPrefixes()
Chris@520 377 {
Chris@520 378 return;
Chris@520 379 //!!!
Chris@520 380 if (m_prefixesLoaded) return;
Chris@520 381 const char *prefixes[] = {
Chris@520 382 "http://purl.org/ontology/vamp/"
Chris@520 383 };
Chris@520 384 for (size_t i = 0; i < sizeof(prefixes)/sizeof(prefixes[0]); ++i) {
Chris@520 385 CachedFile cf(prefixes[i], 0, "application/rdf+xml");
Chris@520 386 if (!cf.isOK()) continue;
Chris@520 387 SimpleSPARQLQuery::addSourceToModel
Chris@520 388 (QUrl::fromLocalFile(cf.getLocalFilename()).toString());
Chris@520 389 }
Chris@520 390 m_prefixesLoaded = true;
Chris@520 391 }
Chris@439 392
Chris@439 393