annotate rdf/PluginRDFIndexer.cpp @ 457:ef14acd6d102

* Add beginnings of capability to search plugins that are not yet installed -- lots more work to do here, though
author Chris Cannam
date Tue, 14 Oct 2008 16:36:35 +0000
parents 64e64e304a12
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