PluginRDFIndexer.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2008-2012 QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "PluginRDFIndexer.h"
17 
18 #include "data/fileio/CachedFile.h"
19 #include "data/fileio/FileSource.h"
22 
23 #include "base/Profiler.h"
24 #include "base/Debug.h"
25 
26 #include <vamp-hostsdk/PluginHostAdapter.h>
27 
28 #include <dataquay/BasicStore.h>
29 #include <dataquay/RDFException.h>
30 
31 #include <QFileInfo>
32 #include <QDir>
33 #include <QUrl>
34 #include <QDateTime>
35 #include <QSettings>
36 #include <QFile>
37 
38 #include <iostream>
39 
40 using std::vector;
41 using std::string;
42 using Vamp::PluginHostAdapter;
43 
44 using Dataquay::Uri;
45 using Dataquay::Node;
46 using Dataquay::Nodes;
47 using Dataquay::Triple;
48 using Dataquay::Triples;
49 using Dataquay::BasicStore;
50 using Dataquay::RDFException;
51 using Dataquay::RDFDuplicateImportException;
52 
55 
58 {
60  return m_instance;
61 }
62 
64  m_index(new Dataquay::BasicStore)
65 {
66  m_index->addPrefix("vamp", Uri("http://purl.org/ontology/vamp/"));
67  m_index->addPrefix("foaf", Uri("http://xmlns.com/foaf/0.1/"));
68  m_index->addPrefix("dc", Uri("http://purl.org/dc/elements/1.1/"));
70 }
71 
72 const BasicStore *
74 {
75  return m_index;
76 }
77 
79 {
80  QMutexLocker locker(&m_mutex);
81 }
82 
83 void
85 {
86  vector<string> paths = PluginHostAdapter::getPluginPath();
87 
88 // SVDEBUG << "\nPluginRDFIndexer::indexInstalledURLs: pid is " << getpid() << endl;
89 
90  QStringList filters;
91  filters << "*.ttl";
92  filters << "*.TTL";
93  filters << "*.n3";
94  filters << "*.N3";
95  filters << "*.rdf";
96  filters << "*.RDF";
97 
98  // Search each Vamp plugin path for an RDF file that either has
99  // name "soname", "soname:label" or "soname/label" plus RDF
100  // extension. Use that order of preference, and prefer ttl over
101  // n3 over rdf extension.
102 
103  for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
104 
105  QDir dir(i->c_str());
106  if (!dir.exists()) continue;
107 
108  QStringList entries = dir.entryList
109  (filters, QDir::Files | QDir::Readable);
110 
111  for (QStringList::const_iterator j = entries.begin();
112  j != entries.end(); ++j) {
113 
114  QFileInfo fi(dir.filePath(*j));
115  pullFile(fi.absoluteFilePath());
116  }
117 
118  QStringList subdirs = dir.entryList
119  (QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Readable);
120 
121  for (QStringList::const_iterator j = subdirs.begin();
122  j != subdirs.end(); ++j) {
123 
124  QDir subdir(dir.filePath(*j));
125  if (subdir.exists()) {
126  entries = subdir.entryList
127  (filters, QDir::Files | QDir::Readable);
128  for (QStringList::const_iterator k = entries.begin();
129  k != entries.end(); ++k) {
130  QFileInfo fi(subdir.filePath(*k));
131  pullFile(fi.absoluteFilePath());
132  }
133  }
134  }
135  }
136 
137  reindex();
138 }
139 
140 bool
142 {
143  SVDEBUG << "PluginRDFIndexer::indexConfiguredURLs" << endl;
144 
145  QSettings settings;
146  settings.beginGroup("RDF");
147 
148  QString indexKey("rdf-indices");
149  QStringList indices = settings.value(indexKey).toStringList();
150 
151  for (int i = 0; i < indices.size(); ++i) {
152 
153  QString index = indices[i];
154 
155  SVDEBUG << "PluginRDFIndexer::indexConfiguredURLs: index url is "
156  << index << endl;
157 
158  CachedFile cf(index);
159  if (!cf.isOK()) continue;
160 
161  FileSource indexSource(cf.getLocalFilename());
162 
163  PlaylistFileReader reader(indexSource);
164  if (!reader.isOK()) continue;
165 
166  PlaylistFileReader::Playlist list = reader.load();
167  for (PlaylistFileReader::Playlist::const_iterator j = list.begin();
168  j != list.end(); ++j) {
169  SVDEBUG << "PluginRDFIndexer::indexConfiguredURLs: url is "
170  << *j << endl;
171  pullURL(*j);
172  }
173  }
174 
175  QString urlListKey("rdf-urls");
176  QStringList urls = settings.value(urlListKey).toStringList();
177 
178  for (int i = 0; i < urls.size(); ++i) {
179  pullURL(urls[i]);
180  }
181 
182  settings.endGroup();
183  reindex();
184 
185  return true;
186 }
187 
188 void
190 {
191  // Add more here!
192 
193  Triples packs = m_index->match
194  (Triple(Node(), m_index->expand("vamp:available_library"), Node()));
195 
196  for (Triple packt: packs) {
197  Triples libraries = m_index->match
198  (Triple(packt.object(), m_index->expand("a"),
199  m_index->expand("vamp:PluginLibrary")));
200  if (libraries.empty()) {
201  SVCERR << "WARNING: Plugin pack " << packt.subject()
202  << " claims to contain library " << packt.object()
203  << " which is not known to us as a vamp:PluginLibrary"
204  << endl;
205  }
206  }
207 }
208 
209 QString
211 {
212  QMutexLocker locker(&m_mutex);
213 
214  if (m_idToUriMap.find(pluginId) == m_idToUriMap.end()) return "";
215  return m_idToUriMap[pluginId];
216 }
217 
218 QString
220 {
221  m_mutex.lock();
222 
223  if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
224 
225  m_mutex.unlock();
226 
227  // Haven't found this uri referenced in any document on the
228  // local filesystem; try resolving the pre-fragment part of
229  // the uri as a document URL and reading that if possible.
230 
231  // Because we may want to refer to this document again, we
232  // cache it locally if it turns out to exist.
233 
234  SVDEBUG << "PluginRDFIndexer::getIdForPluginURI: NOTE: Failed to find a local RDF document describing plugin <" << uri << ">: attempting to retrieve one remotely by guesswork" << endl;
235 
236  QString baseUrl = QUrl(uri).toString(QUrl::RemoveFragment);
237 
238  indexURL(baseUrl);
239 
240  m_mutex.lock();
241 
242  if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
243  m_uriToIdMap[uri] = "";
244  }
245  }
246 
247  QString id = m_uriToIdMap[uri];
248  m_mutex.unlock();
249  return id;
250 }
251 
252 QStringList
254 {
255  QMutexLocker locker(&m_mutex);
256 
257  QStringList ids;
258  for (StringMap::const_iterator i = m_idToUriMap.begin();
259  i != m_idToUriMap.end(); ++i) {
260  ids.push_back(i->first);
261  }
262  return ids;
263 }
264 
265 bool
266 PluginRDFIndexer::pullFile(QString filepath)
267 {
268  QUrl url = QUrl::fromLocalFile(filepath);
269  QString urlString = url.toString();
270  return pullURL(urlString);
271 }
272 
273 bool
274 PluginRDFIndexer::indexURL(QString urlString)
275 {
276  bool pulled = pullURL(urlString);
277  if (!pulled) return false;
278  reindex();
279  return true;
280 }
281 
282 bool
283 PluginRDFIndexer::pullURL(QString urlString)
284 {
285  Profiler profiler("PluginRDFIndexer::indexURL");
286 
287 // SVDEBUG << "PluginRDFIndexer::indexURL(" << urlString << ")" << endl;
288 
289  QMutexLocker locker(&m_mutex);
290 
291  QUrl local = urlString;
292 
293  if (FileSource::isRemote(urlString) &&
294  FileSource::canHandleScheme(urlString)) {
295 
296  CachedFile cf(urlString, nullptr, "application/rdf+xml");
297  if (!cf.isOK()) {
298  return false;
299  }
300 
301  local = QUrl::fromLocalFile(cf.getLocalFilename());
302 
303  } else if (urlString.startsWith("file:")) {
304 
305  local = QUrl(urlString);
306 
307  } else {
308 
309  local = QUrl::fromLocalFile(urlString);
310  }
311 
312  try {
313  m_index->import(local, BasicStore::ImportIgnoreDuplicates);
314  } catch (RDFException &e) {
315  SVDEBUG << e.what() << endl;
316  SVDEBUG << "PluginRDFIndexer::pullURL: Failed to import document from "
317  << urlString << ": " << e.what() << endl;
318  return false;
319  }
320  return true;
321 }
322 
323 bool
325 {
326  Triples tt = m_index->match
327  (Triple(Node(), Uri("a"), m_index->expand("vamp:Plugin")));
328  Nodes plugins = tt.subjects();
329 
330  bool foundSomething = false;
331  bool addedSomething = false;
332 
333  foreach (Node plugin, plugins) {
334 
335  if (plugin.type != Node::URI) {
336  SVDEBUG << "PluginRDFIndexer::reindex: Plugin has no URI: node is "
337  << plugin << endl;
338  continue;
339  }
340 
341  Node idn = m_index->complete
342  (Triple(plugin, m_index->expand("vamp:identifier"), Node()));
343 
344  if (idn.type != Node::Literal) {
345  SVDEBUG << "PluginRDFIndexer::reindex: Plugin " << plugin
346  << " lacks vamp:identifier literal" << endl;
347  continue;
348  }
349 
350  Node libn = m_index->complete
351  (Triple(Node(), m_index->expand("vamp:available_plugin"), plugin));
352 
353  if (libn.type != Node::URI) {
354  SVDEBUG << "PluginRDFIndexer::reindex: Plugin " << plugin
355  << " is not vamp:available_plugin in any library" << endl;
356  continue;
357  }
358 
359  Node son = m_index->complete
360  (Triple(libn, m_index->expand("vamp:identifier"), Node()));
361 
362  if (son.type != Node::Literal) {
363  SVDEBUG << "PluginRDFIndexer::reindex: Library " << libn
364  << " lacks vamp:identifier for soname" << endl;
365  continue;
366  }
367 
368  QString pluginUri = plugin.value;
369  QString identifier = idn.value;
370  QString soname = son.value;
371 
372  QString pluginId = PluginIdentifier::createIdentifier
373  ("vamp", soname, identifier);
374 
375  foundSomething = true;
376 
377  if (m_idToUriMap.find(pluginId) != m_idToUriMap.end()) {
378  continue;
379  }
380 
381  m_idToUriMap[pluginId] = pluginUri;
382 
383  addedSomething = true;
384 
385  if (pluginUri != "") {
386  if (m_uriToIdMap.find(pluginUri) != m_uriToIdMap.end()) {
387  SVDEBUG << "PluginRDFIndexer::reindex: WARNING: Found multiple plugins with the same URI:" << endl;
388  SVDEBUG << " 1. Plugin id \"" << m_uriToIdMap[pluginUri] << "\"" << endl;
389  SVDEBUG << " 2. Plugin id \"" << pluginId << "\"" << endl;
390  SVDEBUG << "both claim URI <" << pluginUri << ">" << endl;
391  } else {
392  m_uriToIdMap[pluginUri] = pluginId;
393  }
394  }
395  }
396 
397  if (!foundSomething) {
398  SVDEBUG << "PluginRDFIndexer::reindex: NOTE: Plugins found, but none sufficiently described" << endl;
399  }
400 
401  return addedSomething;
402 }
QString getURIForPluginId(QString pluginId)
bool isRemote() const
Return true if this FileSource is referring to a remote URL.
Definition: FileSource.cpp:610
std::vector< QString > Playlist
QString getIdForPluginURI(QString uri)
Dataquay::BasicStore * m_index
StringMap m_idToUriMap
static QString createIdentifier(QString type, QString soName, QString label)
QString getLocalFilename() const
Definition: CachedFile.cpp:112
const Dataquay::BasicStore * getIndex()
QStringList getIndexedPluginIds()
FileSource is a class used to refer to the contents of a file that may be either local or at a remote...
Definition: FileSource.h:59
bool isOK() const
Definition: CachedFile.cpp:106
#define SVDEBUG
Definition: Debug.h:106
void performConsistencyChecks()
Perform various checks for consistency of RDF definitions, printing warnings to stderr/logfile as app...
bool indexURL(QString url)
static bool canHandleScheme(QUrl url)
Return true if FileSource can handle the retrieval scheme for the given URL (or if the URL is for a l...
Definition: FileSource.cpp:534
#define SVCERR
Definition: Debug.h:109
bool indexConfiguredURLs()
Index all URLs obtained from index files defined in the current settings.
StringMap m_uriToIdMap
static PluginRDFIndexer * getInstance()
bool pullFile(QString path)
bool pullURL(QString urlString)
static PluginRDFIndexer * m_instance
Profile point instance class.
Definition: Profiler.h:93