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