diff rdf/PluginRDFIndexer.cpp @ 439:beb2948baa77

* Merge revisions 1041 to 1130 from sv-rdf-import branch
author Chris Cannam
date Thu, 18 Sep 2008 12:09:32 +0000
parents
children 5746c559af15
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rdf/PluginRDFIndexer.cpp	Thu Sep 18 12:09:32 2008 +0000
@@ -0,0 +1,286 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2008 QMUL.
+   
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "PluginRDFIndexer.h"
+
+#include "SimpleSPARQLQuery.h"
+
+#include "data/fileio/FileSource.h"
+#include "plugin/PluginIdentifier.h"
+
+#include <vamp-sdk/PluginHostAdapter.h>
+
+#include <QFileInfo>
+#include <QDir>
+#include <QUrl>
+
+#include <iostream>
+using std::cerr;
+using std::endl;
+using std::vector;
+using std::string;
+using Vamp::PluginHostAdapter;
+
+PluginRDFIndexer *
+PluginRDFIndexer::m_instance = 0;
+
+PluginRDFIndexer *
+PluginRDFIndexer::getInstance() 
+{
+    if (!m_instance) m_instance = new PluginRDFIndexer();
+    return m_instance;
+}
+
+PluginRDFIndexer::PluginRDFIndexer()
+{
+    vector<string> paths = PluginHostAdapter::getPluginPath();
+
+    QStringList filters;
+    filters << "*.n3";
+    filters << "*.N3";
+    filters << "*.rdf";
+    filters << "*.RDF";
+
+    // Search each Vamp plugin path for a .rdf file that either has
+    // name "soname", "soname:label" or "soname/label" plus RDF
+    // extension.  Use that order of preference, and prefer n3 over
+    // rdf extension.
+
+    for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
+        
+        QDir dir(i->c_str());
+        if (!dir.exists()) continue;
+
+        QStringList entries = dir.entryList
+            (filters, QDir::Files | QDir::Readable);
+
+        for (QStringList::const_iterator j = entries.begin();
+             j != entries.end(); ++j) {
+            QFileInfo fi(dir.filePath(*j));
+            indexFile(fi.absoluteFilePath());
+        }
+
+        QStringList subdirs = dir.entryList
+            (QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Readable);
+
+        for (QStringList::const_iterator j = subdirs.begin();
+             j != subdirs.end(); ++j) {
+            QDir subdir(dir.filePath(*j));
+            if (subdir.exists()) {
+                entries = subdir.entryList
+                    (filters, QDir::Files | QDir::Readable);
+                for (QStringList::const_iterator k = entries.begin();
+                     k != entries.end(); ++k) {
+                    QFileInfo fi(subdir.filePath(*k));
+                    indexFile(fi.absoluteFilePath());
+                }
+            }
+        }
+    }
+}
+
+PluginRDFIndexer::~PluginRDFIndexer()
+{
+    while (!m_cache.empty()) {
+        delete *m_cache.begin();
+        m_cache.erase(m_cache.begin());
+    }
+}
+
+QString
+PluginRDFIndexer::getURIForPluginId(QString pluginId)
+{
+    if (m_idToUriMap.find(pluginId) == m_idToUriMap.end()) return "";
+    return m_idToUriMap[pluginId];
+}
+
+QString
+PluginRDFIndexer::getIdForPluginURI(QString uri)
+{
+    if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
+
+        // Haven't found this uri referenced in any document on the
+        // local filesystem; try resolving the pre-fragment part of
+        // the uri as a document URL and reading that if possible.
+
+        // Because we may want to refer to this document again, we
+        // cache it locally if it turns out to exist.
+
+        cerr << "PluginRDFIndexer::getIdForPluginURI: NOTE: Failed to find a local RDF document describing plugin <" << uri.toStdString() << ">: attempting to retrieve one remotely by guesswork" << endl;
+
+        QString baseUrl = QUrl(uri).toString(QUrl::RemoveFragment);
+
+        FileSource source(baseUrl);
+        if (source.isAvailable()) {
+            source.waitForData();
+            if (indexFile(source.getLocalFilename())) {
+                m_cache.insert(new FileSource(source));
+            }
+        }
+
+        if (m_uriToIdMap.find(uri) == m_uriToIdMap.end()) {
+            m_uriToIdMap[uri] = "";
+        }
+    }
+
+    return m_uriToIdMap[uri];
+}
+
+QString
+PluginRDFIndexer::getDescriptionURLForPluginId(QString pluginId)
+{
+    if (m_idToDescriptionMap.find(pluginId) == m_idToDescriptionMap.end()) return "";
+    return m_idToDescriptionMap[pluginId];
+}
+
+QString
+PluginRDFIndexer::getDescriptionURLForPluginURI(QString uri)
+{
+    QString id = getIdForPluginURI(uri);
+    if (id == "") return "";
+    return getDescriptionURLForPluginId(id);
+}
+
+bool
+PluginRDFIndexer::indexFile(QString filepath)
+{
+    QUrl url = QUrl::fromLocalFile(filepath);
+    QString urlString = url.toString();
+    return indexURL(urlString);
+}
+
+bool
+PluginRDFIndexer::indexURL(QString urlString)
+{
+//    cerr << "PluginRDFIndexer::indexURL: url = <" << urlString.toStdString() << ">" << endl;
+
+    SimpleSPARQLQuery query
+        (QString
+         (
+             " PREFIX vamp: <http://purl.org/ontology/vamp/> "
+
+             " SELECT ?plugin ?library_id ?plugin_id "
+             " FROM <%1> "
+
+             " WHERE { "
+             "   ?plugin a vamp:Plugin . "
+
+             // Make the identifier and library parts optional, so
+             // that we can check and report helpfully if one or both
+             // is absent instead of just getting no results
+
+             "   OPTIONAL { ?plugin vamp:identifier ?plugin_id } . "
+
+             "   OPTIONAL { "
+             "     ?library a vamp:PluginLibrary ; "
+             "              vamp:available_plugin ?plugin ; "
+             "              vamp:identifier ?library_id "
+             "   } "
+             " } "
+             )
+         .arg(urlString));
+
+    SimpleSPARQLQuery::ResultList results = query.execute();
+
+    if (!query.isOK()) {
+        cerr << "ERROR: PluginRDFIndexer::indexURL: ERROR: Failed to index document at <"
+             << urlString.toStdString() << ">: "
+             << query.getErrorString().toStdString() << endl;
+        return false;
+    }
+
+    if (results.empty()) {
+        cerr << "PluginRDFIndexer::indexURL: NOTE: Document at <"
+             << urlString.toStdString()
+             << "> does not describe any vamp:Plugin resources" << endl;
+        return false;
+    }
+
+    bool foundSomething = false;
+    bool addedSomething = false;
+
+    for (SimpleSPARQLQuery::ResultList::iterator i = results.begin();
+         i != results.end(); ++i) {
+
+        QString pluginUri = (*i)["plugin"].value;
+        QString soname = (*i)["library_id"].value;
+        QString identifier = (*i)["plugin_id"].value;
+
+        if (identifier == "") {
+            cerr << "PluginRDFIndexer::indexURL: NOTE: Document at <"
+                 << urlString.toStdString()
+                 << "> fails to define any vamp:identifier for plugin <"
+                 << pluginUri.toStdString() << ">"
+                 << endl;
+            continue;
+        }
+        if (soname == "") {
+            cerr << "PluginRDFIndexer::indexURL: NOTE: Document at <"
+                 << urlString.toStdString() << "> does not associate plugin <"
+                 << pluginUri.toStdString() << "> with any implementation library"
+                 << endl;
+            continue;
+        }
+/*
+        cerr << "PluginRDFIndexer::indexURL: Document for plugin \""
+             << soname.toStdString() << ":" << identifier.toStdString()
+             << "\" (uri <" << pluginUri.toStdString() << ">) is at url <"
+             << urlString.toStdString() << ">" << endl;
+*/
+        QString pluginId = PluginIdentifier::createIdentifier
+            ("vamp", soname, identifier);
+
+        foundSomething = true;
+
+        if (m_idToDescriptionMap.find(pluginId) != m_idToDescriptionMap.end()) {
+            cerr << "PluginRDFIndexer::indexURL: NOTE: Plugin id \""
+                 << pluginId.toStdString() << "\", described in document at <"
+                 << urlString.toStdString()
+                 << ">, has already been described in document <"
+                 << m_idToDescriptionMap[pluginId].toStdString()
+                 << ">: ignoring this new description" << endl;
+            continue;
+        }
+
+        m_idToDescriptionMap[pluginId] = urlString;
+        m_idToUriMap[pluginId] = pluginUri;
+
+        addedSomething = true;
+
+        if (pluginUri != "") {
+            if (m_uriToIdMap.find(pluginUri) != m_uriToIdMap.end()) {
+                cerr << "PluginRDFIndexer::indexURL: WARNING: Found multiple plugins with the same URI:" << endl;
+                cerr << "  1. Plugin id \"" << m_uriToIdMap[pluginUri].toStdString() << "\"" << endl;
+                cerr << "     described in <" << m_idToDescriptionMap[m_uriToIdMap[pluginUri]].toStdString() << ">" << endl;
+                cerr << "  2. Plugin id \"" << pluginId.toStdString() << "\"" << endl;
+                cerr << "     described in <" << urlString.toStdString() << ">" << endl;
+                cerr << "both claim URI <" << pluginUri.toStdString() << ">" << endl;
+            } else {
+                m_uriToIdMap[pluginUri] = pluginId;
+            }
+        }
+    }
+
+    if (!foundSomething) {
+        cerr << "PluginRDFIndexer::indexURL: NOTE: Document at <"
+             << urlString.toStdString()
+             << "> does not sufficiently describe any plugins" << endl;
+    }
+    
+    return addedSomething;
+}
+
+
+