| 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 "PluginRDFDescription.h" | 
| Chris@439 | 17 | 
| Chris@439 | 18 #include "PluginRDFIndexer.h" | 
| Chris@439 | 19 #include "SimpleSPARQLQuery.h" | 
| Chris@439 | 20 | 
| Chris@457 | 21 #include "data/fileio/FileSource.h" | 
| Chris@468 | 22 #include "data/fileio/CachedFile.h" | 
| Chris@457 | 23 | 
| Chris@457 | 24 #include "base/Profiler.h" | 
| Chris@457 | 25 | 
| Chris@439 | 26 #include "plugin/PluginIdentifier.h" | 
| Chris@439 | 27 | 
| Chris@439 | 28 #include <iostream> | 
| Chris@439 | 29 using std::cerr; | 
| Chris@439 | 30 using std::endl; | 
| Chris@439 | 31 | 
| Chris@439 | 32 PluginRDFDescription::PluginRDFDescription(QString pluginId) : | 
| Chris@439 | 33     m_pluginId(pluginId), | 
| Chris@439 | 34     m_haveDescription(false) | 
| Chris@439 | 35 { | 
| Chris@439 | 36     PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance(); | 
| Chris@439 | 37     QString url = indexer->getDescriptionURLForPluginId(pluginId); | 
| Chris@439 | 38     if (url == "") { | 
| Chris@439 | 39         cerr << "PluginRDFDescription: WARNING: No RDF description available for plugin ID \"" | 
| Chris@439 | 40              << pluginId.toStdString() << "\"" << endl; | 
| Chris@439 | 41     } else { | 
| Chris@439 | 42         if (!indexURL(url)) { | 
| Chris@439 | 43             cerr << "PluginRDFDescription: ERROR: Failed to query RDF description for plugin ID \"" | 
| Chris@439 | 44                  << pluginId.toStdString() << "\"" << endl; | 
| Chris@439 | 45         } else { | 
| Chris@439 | 46             m_haveDescription = true; | 
| Chris@439 | 47         } | 
| Chris@439 | 48     } | 
| Chris@439 | 49 } | 
| Chris@439 | 50 | 
| Chris@439 | 51 PluginRDFDescription::~PluginRDFDescription() | 
| Chris@439 | 52 { | 
| Chris@439 | 53 } | 
| Chris@439 | 54 | 
| Chris@439 | 55 bool | 
| Chris@439 | 56 PluginRDFDescription::haveDescription() const | 
| Chris@439 | 57 { | 
| Chris@439 | 58     return m_haveDescription; | 
| Chris@439 | 59 } | 
| Chris@439 | 60 | 
| Chris@457 | 61 QString | 
| Chris@457 | 62 PluginRDFDescription::getPluginName() const | 
| Chris@457 | 63 { | 
| Chris@457 | 64     return m_pluginName; | 
| Chris@457 | 65 } | 
| Chris@457 | 66 | 
| Chris@457 | 67 QString | 
| Chris@457 | 68 PluginRDFDescription::getPluginDescription() const | 
| Chris@457 | 69 { | 
| Chris@457 | 70     return m_pluginDescription; | 
| Chris@457 | 71 } | 
| Chris@457 | 72 | 
| Chris@457 | 73 QString | 
| Chris@457 | 74 PluginRDFDescription::getPluginMaker() const | 
| Chris@457 | 75 { | 
| Chris@457 | 76     return m_pluginMaker; | 
| Chris@457 | 77 } | 
| Chris@457 | 78 | 
| Chris@462 | 79 QString | 
| Chris@462 | 80 PluginRDFDescription::getPluginInfoURL() const | 
| Chris@462 | 81 { | 
| Chris@462 | 82     return m_pluginInfoURL; | 
| Chris@462 | 83 } | 
| Chris@462 | 84 | 
| Chris@457 | 85 QStringList | 
| Chris@457 | 86 PluginRDFDescription::getOutputIds() const | 
| Chris@457 | 87 { | 
| Chris@457 | 88     QStringList ids; | 
| Chris@457 | 89     for (OutputDispositionMap::const_iterator i = m_outputDispositions.begin(); | 
| Chris@457 | 90          i != m_outputDispositions.end(); ++i) { | 
| Chris@457 | 91         ids.push_back(i->first); | 
| Chris@457 | 92     } | 
| Chris@457 | 93     return ids; | 
| Chris@457 | 94 } | 
| Chris@457 | 95 | 
| Chris@457 | 96 QString | 
| Chris@457 | 97 PluginRDFDescription::getOutputName(QString outputId) const | 
| Chris@457 | 98 { | 
| Chris@457 | 99     if (m_outputNames.find(outputId) == m_outputNames.end()) { | 
| Chris@457 | 100         return ""; | 
| Chris@457 | 101     } | 
| Chris@457 | 102     return m_outputNames.find(outputId)->second; | 
| Chris@457 | 103 } | 
| Chris@457 | 104 | 
| Chris@439 | 105 PluginRDFDescription::OutputDisposition | 
| Chris@439 | 106 PluginRDFDescription::getOutputDisposition(QString outputId) const | 
| Chris@439 | 107 { | 
| Chris@439 | 108     if (m_outputDispositions.find(outputId) == m_outputDispositions.end()) { | 
| Chris@439 | 109         return OutputDispositionUnknown; | 
| Chris@439 | 110     } | 
| Chris@439 | 111     return m_outputDispositions.find(outputId)->second; | 
| Chris@439 | 112 } | 
| Chris@439 | 113 | 
| Chris@439 | 114 QString | 
| Chris@439 | 115 PluginRDFDescription::getOutputEventTypeURI(QString outputId) const | 
| Chris@439 | 116 { | 
| Chris@439 | 117     if (m_outputEventTypeURIMap.find(outputId) == | 
| Chris@439 | 118         m_outputEventTypeURIMap.end()) { | 
| Chris@439 | 119         return ""; | 
| Chris@439 | 120     } | 
| Chris@439 | 121     return m_outputEventTypeURIMap.find(outputId)->second; | 
| Chris@439 | 122 } | 
| Chris@439 | 123 | 
| Chris@439 | 124 QString | 
| Chris@440 | 125 PluginRDFDescription::getOutputFeatureAttributeURI(QString outputId) const | 
| Chris@440 | 126 { | 
| Chris@440 | 127     if (m_outputFeatureAttributeURIMap.find(outputId) == | 
| Chris@440 | 128         m_outputFeatureAttributeURIMap.end()) { | 
| Chris@440 | 129         return ""; | 
| Chris@440 | 130     } | 
| Chris@440 | 131     return m_outputFeatureAttributeURIMap.find(outputId)->second; | 
| Chris@440 | 132 } | 
| Chris@440 | 133 | 
| Chris@440 | 134 QString | 
| Chris@440 | 135 PluginRDFDescription::getOutputSignalTypeURI(QString outputId) const | 
| Chris@440 | 136 { | 
| Chris@440 | 137     if (m_outputSignalTypeURIMap.find(outputId) == | 
| Chris@440 | 138         m_outputSignalTypeURIMap.end()) { | 
| Chris@440 | 139         return ""; | 
| Chris@440 | 140     } | 
| Chris@440 | 141     return m_outputSignalTypeURIMap.find(outputId)->second; | 
| Chris@440 | 142 } | 
| Chris@440 | 143 | 
| Chris@440 | 144 QString | 
| Chris@439 | 145 PluginRDFDescription::getOutputUnit(QString outputId) const | 
| Chris@439 | 146 { | 
| Chris@439 | 147     if (m_outputUnitMap.find(outputId) == m_outputUnitMap.end()) { | 
| Chris@439 | 148         return ""; | 
| Chris@439 | 149     } | 
| Chris@439 | 150     return m_outputUnitMap.find(outputId)->second; | 
| Chris@439 | 151 } | 
| Chris@439 | 152 | 
| Chris@439 | 153 bool | 
| Chris@439 | 154 PluginRDFDescription::indexURL(QString url) | 
| Chris@439 | 155 { | 
| Chris@457 | 156     Profiler profiler("PluginRDFDescription::indexURL"); | 
| Chris@457 | 157 | 
| Chris@439 | 158     QString type, soname, label; | 
| Chris@439 | 159     PluginIdentifier::parseIdentifier(m_pluginId, type, soname, label); | 
| Chris@439 | 160 | 
| Chris@457 | 161     bool success = true; | 
| Chris@457 | 162 | 
| Chris@457 | 163     QString local = url; | 
| Chris@457 | 164 | 
| Chris@457 | 165     if (FileSource::isRemote(url) && | 
| Chris@457 | 166         FileSource::canHandleScheme(url)) { | 
| Chris@461 | 167 | 
| Chris@468 | 168         CachedFile cf(url); | 
| Chris@468 | 169         if (!cf.isOK()) { | 
| Chris@468 | 170             return false; | 
| Chris@468 | 171         } | 
| Chris@468 | 172 | 
| Chris@468 | 173         local = QUrl::fromLocalFile(cf.getLocalFilename()).toString(); | 
| Chris@457 | 174     } | 
| Chris@457 | 175 | 
| Chris@457 | 176     if (!indexMetadata(local, label)) success = false; | 
| Chris@457 | 177     if (!indexOutputs(local, label)) success = false; | 
| Chris@457 | 178 | 
| Chris@457 | 179     return success; | 
| Chris@457 | 180 } | 
| Chris@457 | 181 | 
| Chris@457 | 182 bool | 
| Chris@457 | 183 PluginRDFDescription::indexMetadata(QString url, QString label) | 
| Chris@457 | 184 { | 
| Chris@457 | 185     Profiler profiler("PluginRDFDescription::indexMetadata"); | 
| Chris@457 | 186 | 
| Chris@457 | 187     QString queryTemplate = | 
| Chris@457 | 188         QString( | 
| Chris@457 | 189             " PREFIX vamp: <http://purl.org/ontology/vamp/> " | 
| Chris@457 | 190             " PREFIX foaf: <http://xmlns.com/foaf/0.1/> " | 
| Chris@457 | 191             " PREFIX dc: <http://purl.org/dc/elements/1.1/> " | 
| Chris@457 | 192             " SELECT ?%4 FROM <%1> " | 
| Chris@457 | 193             " WHERE { " | 
| Chris@457 | 194             "   ?plugin a vamp:Plugin ; " | 
| Chris@457 | 195             "           vamp:identifier \"%2\" ; " | 
| Chris@457 | 196             "           %3 ?%4 . " | 
| Chris@457 | 197             " }") | 
| Chris@457 | 198         .arg(url) | 
| Chris@457 | 199         .arg(label); | 
| Chris@457 | 200 | 
| Chris@457 | 201     SimpleSPARQLQuery::Value v; | 
| Chris@457 | 202 | 
| Chris@457 | 203     v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@457 | 204         (queryTemplate.arg("vamp:name").arg("name"), "name"); | 
| Chris@457 | 205 | 
| Chris@457 | 206     if (v.type == SimpleSPARQLQuery::LiteralValue && v.value != "") { | 
| Chris@457 | 207         m_pluginName = v.value; | 
| Chris@457 | 208     } | 
| Chris@457 | 209 | 
| Chris@457 | 210     v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@457 | 211         (queryTemplate.arg("dc:description").arg("description"), "description"); | 
| Chris@457 | 212 | 
| Chris@457 | 213     if (v.type == SimpleSPARQLQuery::LiteralValue && v.value != "") { | 
| Chris@457 | 214         m_pluginDescription = v.value; | 
| Chris@457 | 215     } | 
| Chris@457 | 216 | 
| Chris@457 | 217     v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@457 | 218         (QString( | 
| Chris@457 | 219             " PREFIX vamp: <http://purl.org/ontology/vamp/> " | 
| Chris@457 | 220             " PREFIX foaf: <http://xmlns.com/foaf/0.1/> " | 
| Chris@457 | 221             " SELECT ?name FROM <%1> " | 
| Chris@457 | 222             " WHERE { " | 
| Chris@457 | 223             "   ?plugin a vamp:Plugin ; " | 
| Chris@457 | 224             "           vamp:identifier \"%2\" ; " | 
| Chris@457 | 225             "           foaf:maker ?maker . " | 
| Chris@457 | 226             "   ?maker foaf:name ?name . " | 
| Chris@457 | 227             " }") | 
| Chris@457 | 228          .arg(url) | 
| Chris@457 | 229          .arg(label), "name"); | 
| Chris@457 | 230 | 
| Chris@457 | 231     if (v.type == SimpleSPARQLQuery::LiteralValue && v.value != "") { | 
| Chris@457 | 232         m_pluginMaker = v.value; | 
| Chris@457 | 233     } | 
| Chris@457 | 234 | 
| Chris@462 | 235     // If we have a more-information URL for this plugin, then we take | 
| Chris@463 | 236     // that.  Otherwise, a more-information URL for the plugin | 
| Chris@462 | 237     // library would do nicely.  Failing that, we could perhaps use | 
| Chris@462 | 238     // any foaf:page URL at all that appears in the file -- but | 
| Chris@462 | 239     // perhaps that would be unwise | 
| Chris@462 | 240 | 
| Chris@462 | 241     v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@462 | 242         (QString( | 
| Chris@462 | 243             " PREFIX vamp: <http://purl.org/ontology/vamp/> " | 
| Chris@462 | 244             " PREFIX foaf: <http://xmlns.com/foaf/0.1/> " | 
| Chris@462 | 245             " SELECT ?page from <%1> " | 
| Chris@462 | 246             " WHERE { " | 
| Chris@462 | 247             "   ?plugin a vamp:Plugin ; " | 
| Chris@462 | 248             "           vamp:identifier \"%2\" ; " | 
| Chris@462 | 249             "           foaf:page ?page . " | 
| Chris@462 | 250             " }") | 
| Chris@462 | 251          .arg(url) | 
| Chris@462 | 252          .arg(label), "page"); | 
| Chris@462 | 253 | 
| Chris@462 | 254     if (v.type == SimpleSPARQLQuery::URIValue && v.value != "") { | 
| Chris@462 | 255 | 
| Chris@462 | 256         m_pluginInfoURL = v.value; | 
| Chris@462 | 257 | 
| Chris@462 | 258     } else { | 
| Chris@462 | 259 | 
| Chris@462 | 260         v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@462 | 261             (QString( | 
| Chris@462 | 262                 " PREFIX vamp: <http://purl.org/ontology/vamp/> " | 
| Chris@462 | 263                 " PREFIX foaf: <http://xmlns.com/foaf/0.1/> " | 
| Chris@462 | 264                 " SELECT ?page from <%1> " | 
| Chris@462 | 265                 " WHERE { " | 
| Chris@462 | 266                 "   ?library a vamp:PluginLibrary ; " | 
| Chris@462 | 267                 "            vamp:available_plugin ?plugin ; " | 
| Chris@462 | 268                 "            foaf:page ?page . " | 
| Chris@462 | 269                 "   ?plugin a vamp:Plugin ; " | 
| Chris@462 | 270                 "           vamp:identifier \"%2\" . " | 
| Chris@462 | 271                 " }") | 
| Chris@462 | 272              .arg(url) | 
| Chris@462 | 273              .arg(label), "page"); | 
| Chris@462 | 274 | 
| Chris@462 | 275         if (v.type == SimpleSPARQLQuery::URIValue && v.value != "") { | 
| Chris@462 | 276 | 
| Chris@462 | 277             m_pluginInfoURL = v.value; | 
| Chris@462 | 278         } | 
| Chris@462 | 279     } | 
| Chris@462 | 280 | 
| Chris@457 | 281     return true; | 
| Chris@457 | 282 } | 
| Chris@457 | 283 | 
| Chris@457 | 284 bool | 
| Chris@457 | 285 PluginRDFDescription::indexOutputs(QString url, QString label) | 
| Chris@457 | 286 { | 
| Chris@457 | 287     Profiler profiler("PluginRDFDescription::indexOutputs"); | 
| Chris@457 | 288 | 
| Chris@439 | 289     SimpleSPARQLQuery query | 
| Chris@439 | 290         (QString | 
| Chris@439 | 291          ( | 
| Chris@439 | 292              " PREFIX vamp: <http://purl.org/ontology/vamp/> " | 
| Chris@439 | 293 | 
| Chris@440 | 294              " SELECT ?output ?output_id ?output_type ?unit " | 
| Chris@439 | 295              " FROM <%1> " | 
| Chris@439 | 296 | 
| Chris@439 | 297              " WHERE { " | 
| Chris@439 | 298 | 
| Chris@439 | 299              "   ?plugin a vamp:Plugin ; " | 
| Chris@439 | 300              "           vamp:identifier \"%2\" ; " | 
| Chris@440 | 301              "           vamp:output ?output . " | 
| Chris@439 | 302 | 
| Chris@439 | 303              "   ?output vamp:identifier ?output_id ; " | 
| Chris@439 | 304              "           a ?output_type . " | 
| Chris@439 | 305 | 
| Chris@439 | 306              "   OPTIONAL { " | 
| Chris@439 | 307              "     ?output vamp:unit ?unit " | 
| Chris@439 | 308              "   } . " | 
| Chris@439 | 309 | 
| Chris@439 | 310              " } " | 
| Chris@439 | 311              ) | 
| Chris@439 | 312          .arg(url) | 
| Chris@439 | 313          .arg(label)); | 
| Chris@439 | 314 | 
| Chris@439 | 315     SimpleSPARQLQuery::ResultList results = query.execute(); | 
| Chris@439 | 316 | 
| Chris@439 | 317     if (!query.isOK()) { | 
| Chris@439 | 318         cerr << "ERROR: PluginRDFDescription::indexURL: ERROR: Failed to query document at <" | 
| Chris@439 | 319              << url.toStdString() << ">: " | 
| Chris@439 | 320              << query.getErrorString().toStdString() << endl; | 
| Chris@439 | 321         return false; | 
| Chris@439 | 322     } | 
| Chris@439 | 323 | 
| Chris@439 | 324     if (results.empty()) { | 
| Chris@439 | 325         cerr << "ERROR: PluginRDFDescription::indexURL: NOTE: Document at <" | 
| Chris@439 | 326              << url.toStdString() | 
| Chris@440 | 327              << "> does not appear to describe any outputs for plugin with id \"" | 
| Chris@440 | 328              << label.toStdString() << "\"" << endl; | 
| Chris@439 | 329         return false; | 
| Chris@439 | 330     } | 
| Chris@439 | 331 | 
| Chris@439 | 332     // Note that an output may appear more than once, if it inherits | 
| Chris@439 | 333     // more than one type (e.g. DenseOutput and QuantizedOutput).  So | 
| Chris@439 | 334     // these results must accumulate | 
| Chris@439 | 335 | 
| Chris@439 | 336     for (int i = 0; i < results.size(); ++i) { | 
| Chris@439 | 337 | 
| Chris@440 | 338         QString outputUri = results[i]["output"].value; | 
| Chris@439 | 339         QString outputId = results[i]["output_id"].value; | 
| Chris@439 | 340         QString outputType = results[i]["output_type"].value; | 
| Chris@439 | 341 | 
| Chris@439 | 342         if (outputType.contains("DenseOutput")) { | 
| Chris@439 | 343             m_outputDispositions[outputId] = OutputDense; | 
| Chris@439 | 344         } else if (outputType.contains("SparseOutput")) { | 
| Chris@439 | 345             m_outputDispositions[outputId] = OutputSparse; | 
| Chris@439 | 346         } else if (outputType.contains("TrackLevelOutput")) { | 
| Chris@439 | 347             m_outputDispositions[outputId] = OutputTrackLevel; | 
| Chris@457 | 348         } else { | 
| Chris@457 | 349             m_outputDispositions[outputId] = OutputDispositionUnknown; | 
| Chris@439 | 350         } | 
| Chris@439 | 351 | 
| Chris@439 | 352         if (results[i]["unit"].type == SimpleSPARQLQuery::LiteralValue) { | 
| Chris@439 | 353 | 
| Chris@439 | 354             QString unit = results[i]["unit"].value; | 
| Chris@439 | 355 | 
| Chris@439 | 356             if (unit != "") { | 
| Chris@439 | 357                 m_outputUnitMap[outputId] = unit; | 
| Chris@439 | 358             } | 
| Chris@439 | 359         } | 
| Chris@440 | 360 | 
| Chris@457 | 361         SimpleSPARQLQuery::Value v; | 
| Chris@457 | 362 | 
| Chris@457 | 363         v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@457 | 364             (QString(" PREFIX vamp: <http://purl.org/ontology/vamp/> " | 
| Chris@457 | 365                      " PREFIX dc: <http://purl.org/dc/elements/1.1/> " | 
| Chris@457 | 366                      " SELECT ?title FROM <%1> " | 
| Chris@457 | 367                      " WHERE { <%2> dc:title ?title } ") | 
| Chris@457 | 368              .arg(url).arg(outputUri), "title"); | 
| Chris@457 | 369 | 
| Chris@457 | 370         if (v.type == SimpleSPARQLQuery::LiteralValue && v.value != "") { | 
| Chris@457 | 371             m_outputNames[outputId] = v.value; | 
| Chris@457 | 372         } | 
| Chris@457 | 373 | 
| Chris@440 | 374         QString queryTemplate = | 
| Chris@440 | 375             QString(" PREFIX vamp: <http://purl.org/ontology/vamp/> " | 
| Chris@440 | 376                     " SELECT ?%3 FROM <%1> " | 
| Chris@440 | 377                     " WHERE { <%2> vamp:computes_%3 ?%3 } ") | 
| Chris@440 | 378             .arg(url).arg(outputUri); | 
| Chris@440 | 379 | 
| Chris@440 | 380         v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@440 | 381             (queryTemplate.arg("event_type"), "event_type"); | 
| Chris@440 | 382 | 
| Chris@440 | 383         if (v.type == SimpleSPARQLQuery::URIValue && v.value != "") { | 
| Chris@440 | 384             m_outputEventTypeURIMap[outputId] = v.value; | 
| Chris@440 | 385         } | 
| Chris@440 | 386 | 
| Chris@440 | 387         v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@440 | 388             (queryTemplate.arg("feature_attribute"), "feature_attribute"); | 
| Chris@440 | 389 | 
| Chris@440 | 390         if (v.type == SimpleSPARQLQuery::URIValue && v.value != "") { | 
| Chris@440 | 391             m_outputFeatureAttributeURIMap[outputId] = v.value; | 
| Chris@440 | 392         } | 
| Chris@440 | 393 | 
| Chris@440 | 394         v = SimpleSPARQLQuery::singleResultQuery | 
| Chris@440 | 395             (queryTemplate.arg("signal_type"), "signal_type"); | 
| Chris@440 | 396 | 
| Chris@440 | 397         if (v.type == SimpleSPARQLQuery::URIValue && v.value != "") { | 
| Chris@440 | 398             m_outputSignalTypeURIMap[outputId] = v.value; | 
| Chris@440 | 399         } | 
| Chris@439 | 400     } | 
| Chris@439 | 401 | 
| Chris@439 | 402     return true; | 
| Chris@439 | 403 } | 
| Chris@439 | 404 |