annotate rdf/PluginRDFIndexer.cpp @ 458:f60360209e5c

* Fix race condition in FFTFileCache when reading from the same FFT model from multiple threads (e.g. when applying more than one plugin at once)
author Chris Cannam
date Wed, 15 Oct 2008 12:08:02 +0000
parents ef14acd6d102
children 93fb1ebff76b
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@439 20 #include "data/fileio/FileSource.h"
Chris@439 21 #include "plugin/PluginIdentifier.h"
Chris@439 22
Chris@457 23 #include "base/Profiler.h"
Chris@457 24
Chris@439 25 #include <vamp-sdk/PluginHostAdapter.h>
Chris@439 26
Chris@439 27 #include <QFileInfo>
Chris@439 28 #include <QDir>
Chris@439 29 #include <QUrl>
Chris@439 30
Chris@439 31 #include <iostream>
Chris@439 32 using std::cerr;
Chris@439 33 using std::endl;
Chris@439 34 using std::vector;
Chris@439 35 using std::string;
Chris@439 36 using Vamp::PluginHostAdapter;
Chris@439 37
Chris@439 38 PluginRDFIndexer *
Chris@439 39 PluginRDFIndexer::m_instance = 0;
Chris@439 40
Chris@439 41 PluginRDFIndexer *
Chris@439 42 PluginRDFIndexer::getInstance()
Chris@439 43 {
Chris@439 44 if (!m_instance) m_instance = new PluginRDFIndexer();
Chris@439 45 return m_instance;
Chris@439 46 }
Chris@439 47
Chris@439 48 PluginRDFIndexer::PluginRDFIndexer()
Chris@439 49 {
Chris@439 50 vector<string> paths = PluginHostAdapter::getPluginPath();
Chris@439 51
Chris@439 52 QStringList filters;
Chris@439 53 filters << "*.n3";
Chris@439 54 filters << "*.N3";
Chris@439 55 filters << "*.rdf";
Chris@439 56 filters << "*.RDF";
Chris@439 57
Chris@439 58 // Search each Vamp plugin path for a .rdf file that either has
Chris@439 59 // name "soname", "soname:label" or "soname/label" plus RDF
Chris@439 60 // extension. Use that order of preference, and prefer n3 over
Chris@439 61 // rdf extension.
Chris@439 62
Chris@439 63 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
Chris@439 64
Chris@439 65 QDir dir(i->c_str());
Chris@439 66 if (!dir.exists()) continue;
Chris@439 67
Chris@439 68 QStringList entries = dir.entryList
Chris@439 69 (filters, QDir::Files | QDir::Readable);
Chris@439 70
Chris@439 71 for (QStringList::const_iterator j = entries.begin();
Chris@439 72 j != entries.end(); ++j) {
Chris@439 73 QFileInfo fi(dir.filePath(*j));
Chris@439 74 indexFile(fi.absoluteFilePath());
Chris@439 75 }
Chris@439 76
Chris@439 77 QStringList subdirs = dir.entryList
Chris@439 78 (QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Readable);
Chris@439 79
Chris@439 80 for (QStringList::const_iterator j = subdirs.begin();
Chris@439 81 j != subdirs.end(); ++j) {
Chris@439 82 QDir subdir(dir.filePath(*j));
Chris@439 83 if (subdir.exists()) {
Chris@439 84 entries = subdir.entryList
Chris@439 85 (filters, QDir::Files | QDir::Readable);
Chris@439 86 for (QStringList::const_iterator k = entries.begin();
Chris@439 87 k != entries.end(); ++k) {
Chris@439 88 QFileInfo fi(subdir.filePath(*k));
Chris@439 89 indexFile(fi.absoluteFilePath());
Chris@439 90 }
Chris@439 91 }
Chris@439 92 }
Chris@439 93 }
Chris@439 94 }
Chris@439 95
Chris@439 96 PluginRDFIndexer::~PluginRDFIndexer()
Chris@439 97 {
Chris@457 98 while (!m_sources.empty()) {
Chris@457 99 delete *m_sources.begin();
Chris@457 100 m_sources.erase(m_sources.begin());
Chris@439 101 }
Chris@439 102 }
Chris@439 103
Chris@439 104 QString
Chris@439 105 PluginRDFIndexer::getURIForPluginId(QString pluginId)
Chris@439 106 {
Chris@439 107 if (m_idToUriMap.find(pluginId) == m_idToUriMap.end()) return "";
Chris@439 108 return m_idToUriMap[pluginId];
Chris@439 109 }
Chris@439 110
Chris@439 111 QString
Chris@439 112 PluginRDFIndexer::getIdForPluginURI(QString uri)
Chris@439 113 {
Chris@439 114 if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
Chris@439 115
Chris@439 116 // Haven't found this uri referenced in any document on the
Chris@439 117 // local filesystem; try resolving the pre-fragment part of
Chris@439 118 // the uri as a document URL and reading that if possible.
Chris@439 119
Chris@439 120 // Because we may want to refer to this document again, we
Chris@439 121 // cache it locally if it turns out to exist.
Chris@439 122
Chris@439 123 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 124
Chris@439 125 QString baseUrl = QUrl(uri).toString(QUrl::RemoveFragment);
Chris@439 126
Chris@457 127 indexURL(baseUrl);
Chris@439 128
Chris@439 129 if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
Chris@439 130 m_uriToIdMap[uri] = "";
Chris@439 131 }
Chris@439 132 }
Chris@439 133
Chris@439 134 return m_uriToIdMap[uri];
Chris@439 135 }
Chris@439 136
Chris@439 137 QString
Chris@439 138 PluginRDFIndexer::getDescriptionURLForPluginId(QString pluginId)
Chris@439 139 {
Chris@439 140 if (m_idToDescriptionMap.find(pluginId) == m_idToDescriptionMap.end()) return "";
Chris@439 141 return m_idToDescriptionMap[pluginId];
Chris@439 142 }
Chris@439 143
Chris@439 144 QString
Chris@439 145 PluginRDFIndexer::getDescriptionURLForPluginURI(QString uri)
Chris@439 146 {
Chris@439 147 QString id = getIdForPluginURI(uri);
Chris@439 148 if (id == "") return "";
Chris@439 149 return getDescriptionURLForPluginId(id);
Chris@439 150 }
Chris@439 151
Chris@456 152 QStringList
Chris@456 153 PluginRDFIndexer::getIndexedPluginIds()
Chris@456 154 {
Chris@456 155 QStringList ids;
Chris@456 156 for (StringMap::const_iterator i = m_idToDescriptionMap.begin();
Chris@456 157 i != m_idToDescriptionMap.end(); ++i) {
Chris@456 158 ids.push_back(i->first);
Chris@456 159 }
Chris@456 160 return ids;
Chris@456 161 }
Chris@456 162
Chris@439 163 bool
Chris@439 164 PluginRDFIndexer::indexFile(QString filepath)
Chris@439 165 {
Chris@439 166 QUrl url = QUrl::fromLocalFile(filepath);
Chris@439 167 QString urlString = url.toString();
Chris@439 168 return indexURL(urlString);
Chris@439 169 }
Chris@439 170
Chris@439 171 bool
Chris@439 172 PluginRDFIndexer::indexURL(QString urlString)
Chris@439 173 {
Chris@457 174 Profiler profiler("PluginRDFIndexer::indexURL");
Chris@457 175
Chris@457 176 QString localString = urlString;
Chris@457 177
Chris@457 178 if (FileSource::isRemote(urlString) &&
Chris@457 179 FileSource::canHandleScheme(urlString)) {
Chris@457 180
Chris@457 181 FileSource *source = new FileSource(urlString);
Chris@457 182 if (!source->isAvailable()) {
Chris@457 183 delete source;
Chris@457 184 return false;
Chris@457 185 }
Chris@457 186 source->waitForData();
Chris@457 187 localString = QUrl::fromLocalFile(source->getLocalFilename()).toString();
Chris@457 188 m_sources.insert(source);
Chris@457 189 }
Chris@457 190
Chris@439 191 // cerr << "PluginRDFIndexer::indexURL: url = <" << urlString.toStdString() << ">" << endl;
Chris@439 192
Chris@439 193 SimpleSPARQLQuery query
Chris@439 194 (QString
Chris@439 195 (
Chris@439 196 " PREFIX vamp: <http://purl.org/ontology/vamp/> "
Chris@439 197
Chris@439 198 " SELECT ?plugin ?library_id ?plugin_id "
Chris@439 199 " FROM <%1> "
Chris@439 200
Chris@439 201 " WHERE { "
Chris@439 202 " ?plugin a vamp:Plugin . "
Chris@439 203
Chris@439 204 // Make the identifier and library parts optional, so
Chris@439 205 // that we can check and report helpfully if one or both
Chris@439 206 // is absent instead of just getting no results
Chris@439 207
Chris@440 208 //!!! No -- because of rasqal's inability to correctly
Chris@440 209 // handle more than one OPTIONAL graph in a query, let's
Chris@440 210 // make identifier compulsory after all
Chris@440 211 //" OPTIONAL { ?plugin vamp:identifier ?plugin_id } . "
Chris@440 212
Chris@440 213 " ?plugin vamp:identifier ?plugin_id . "
Chris@439 214
Chris@439 215 " OPTIONAL { "
Chris@439 216 " ?library a vamp:PluginLibrary ; "
Chris@439 217 " vamp:available_plugin ?plugin ; "
Chris@439 218 " vamp:identifier ?library_id "
Chris@439 219 " } "
Chris@439 220 " } "
Chris@439 221 )
Chris@457 222 .arg(localString));
Chris@439 223
Chris@439 224 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@439 225
Chris@439 226 if (!query.isOK()) {
Chris@439 227 cerr << "ERROR: PluginRDFIndexer::indexURL: ERROR: Failed to index document at <"
Chris@439 228 << urlString.toStdString() << ">: "
Chris@439 229 << query.getErrorString().toStdString() << endl;
Chris@439 230 return false;
Chris@439 231 }
Chris@439 232
Chris@439 233 if (results.empty()) {
Chris@439 234 cerr << "PluginRDFIndexer::indexURL: NOTE: Document at <"
Chris@439 235 << urlString.toStdString()
Chris@439 236 << "> does not describe any vamp:Plugin resources" << endl;
Chris@439 237 return false;
Chris@439 238 }
Chris@439 239
Chris@439 240 bool foundSomething = false;
Chris@439 241 bool addedSomething = false;
Chris@439 242
Chris@439 243 for (SimpleSPARQLQuery::ResultList::iterator i = results.begin();
Chris@439 244 i != results.end(); ++i) {
Chris@439 245
Chris@439 246 QString pluginUri = (*i)["plugin"].value;
Chris@439 247 QString soname = (*i)["library_id"].value;
Chris@439 248 QString identifier = (*i)["plugin_id"].value;
Chris@439 249
Chris@439 250 if (identifier == "") {
Chris@439 251 cerr << "PluginRDFIndexer::indexURL: NOTE: Document at <"
Chris@439 252 << urlString.toStdString()
Chris@439 253 << "> fails to define any vamp:identifier for plugin <"
Chris@439 254 << pluginUri.toStdString() << ">"
Chris@439 255 << endl;
Chris@439 256 continue;
Chris@439 257 }
Chris@439 258 if (soname == "") {
Chris@439 259 cerr << "PluginRDFIndexer::indexURL: NOTE: Document at <"
Chris@439 260 << urlString.toStdString() << "> does not associate plugin <"
Chris@439 261 << pluginUri.toStdString() << "> with any implementation library"
Chris@439 262 << endl;
Chris@439 263 continue;
Chris@439 264 }
Chris@439 265 /*
Chris@439 266 cerr << "PluginRDFIndexer::indexURL: Document for plugin \""
Chris@439 267 << soname.toStdString() << ":" << identifier.toStdString()
Chris@439 268 << "\" (uri <" << pluginUri.toStdString() << ">) is at url <"
Chris@439 269 << urlString.toStdString() << ">" << endl;
Chris@439 270 */
Chris@439 271 QString pluginId = PluginIdentifier::createIdentifier
Chris@439 272 ("vamp", soname, identifier);
Chris@439 273
Chris@439 274 foundSomething = true;
Chris@439 275
Chris@439 276 if (m_idToDescriptionMap.find(pluginId) != m_idToDescriptionMap.end()) {
Chris@439 277 cerr << "PluginRDFIndexer::indexURL: NOTE: Plugin id \""
Chris@439 278 << pluginId.toStdString() << "\", described in document at <"
Chris@439 279 << urlString.toStdString()
Chris@439 280 << ">, has already been described in document <"
Chris@439 281 << m_idToDescriptionMap[pluginId].toStdString()
Chris@439 282 << ">: ignoring this new description" << endl;
Chris@439 283 continue;
Chris@439 284 }
Chris@439 285
Chris@439 286 m_idToDescriptionMap[pluginId] = urlString;
Chris@439 287 m_idToUriMap[pluginId] = pluginUri;
Chris@439 288
Chris@439 289 addedSomething = true;
Chris@439 290
Chris@439 291 if (pluginUri != "") {
Chris@439 292 if (m_uriToIdMap.find(pluginUri) != m_uriToIdMap.end()) {
Chris@439 293 cerr << "PluginRDFIndexer::indexURL: WARNING: Found multiple plugins with the same URI:" << endl;
Chris@439 294 cerr << " 1. Plugin id \"" << m_uriToIdMap[pluginUri].toStdString() << "\"" << endl;
Chris@439 295 cerr << " described in <" << m_idToDescriptionMap[m_uriToIdMap[pluginUri]].toStdString() << ">" << endl;
Chris@439 296 cerr << " 2. Plugin id \"" << pluginId.toStdString() << "\"" << endl;
Chris@439 297 cerr << " described in <" << urlString.toStdString() << ">" << endl;
Chris@439 298 cerr << "both claim URI <" << pluginUri.toStdString() << ">" << endl;
Chris@439 299 } else {
Chris@439 300 m_uriToIdMap[pluginUri] = pluginId;
Chris@439 301 }
Chris@439 302 }
Chris@439 303 }
Chris@439 304
Chris@439 305 if (!foundSomething) {
Chris@439 306 cerr << "PluginRDFIndexer::indexURL: NOTE: Document at <"
Chris@439 307 << urlString.toStdString()
Chris@439 308 << "> does not sufficiently describe any plugins" << endl;
Chris@439 309 }
Chris@439 310
Chris@439 311 return addedSomething;
Chris@439 312 }
Chris@439 313
Chris@439 314
Chris@439 315