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@687
|
120 DEBUG << "PluginRDFIndexer::indexConfiguredURLs" << 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@687
|
132 DEBUG << "PluginRDFIndexer::indexConfiguredURLs: index url is "
|
Chris@687
|
133 << index << 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@687
|
146 DEBUG << "PluginRDFIndexer::indexConfiguredURLs: url is "
|
Chris@687
|
147 << j->toStdString() << 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@686
|
189 cerr << "PluginRDFIndexer::getIdForPluginURI: NOTE: Failed to find a local RDF document describing plugin <" << uri << ">: 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@687
|
244 // DEBUG << "PluginRDFIndexer::indexURL(" << urlString << ")" << 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@686
|
291 << query.getErrorString() << endl;
|
Chris@439
|
292 return false;
|
Chris@439
|
293 }
|
Chris@439
|
294
|
Chris@439
|
295 if (results.empty()) {
|
Chris@687
|
296 DEBUG << "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@687
|
311 DEBUG << "PluginRDFIndexer::reindex: NOTE: No vamp:identifier for plugin <"
|
Chris@686
|
312 << pluginUri << ">"
|
Chris@439
|
313 << endl;
|
Chris@439
|
314 continue;
|
Chris@439
|
315 }
|
Chris@481
|
316 if (soUri == "") {
|
Chris@687
|
317 DEBUG << "PluginRDFIndexer::reindex: NOTE: No implementation library for plugin <"
|
Chris@686
|
318 << pluginUri << ">"
|
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@687
|
337 DEBUG << "PluginRDFIndexer::reindex: NOTE: No identifier for library <"
|
Chris@686
|
338 << soUri << ">"
|
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@687
|
358 DEBUG << "PluginRDFIndexer::reindex: WARNING: Found multiple plugins with the same URI:" << endl;
|
Chris@686
|
359 cerr << " 1. Plugin id \"" << m_uriToIdMap[pluginUri] << "\"" << endl;
|
Chris@686
|
360 cerr << " 2. Plugin id \"" << pluginId << "\"" << endl;
|
Chris@686
|
361 cerr << "both claim URI <" << pluginUri << ">" << 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@687
|
369 DEBUG << "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
|