svcore  1.9
RDFTransformFactory.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2008-2012 QMUL.
00008    
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "RDFTransformFactory.h"
00017 
00018 #include <map>
00019 #include <vector>
00020 
00021 #include <QTextStream>
00022 #include <QUrl>
00023 
00024 #include <iostream>
00025 #include <cmath>
00026 
00027 #include "PluginRDFIndexer.h"
00028 #include "PluginRDFDescription.h"
00029 #include "base/ProgressReporter.h"
00030 #include "plugin/PluginIdentifier.h"
00031 
00032 #include "transform/TransformFactory.h"
00033 
00034 #include <dataquay/BasicStore.h>
00035 #include <dataquay/PropertyObject.h>
00036 
00037 using Dataquay::Uri;
00038 using Dataquay::Node;
00039 using Dataquay::Nodes;
00040 using Dataquay::Triple;
00041 using Dataquay::Triples;
00042 using Dataquay::BasicStore;
00043 using Dataquay::PropertyObject;
00044 
00045 
00046 class RDFTransformFactoryImpl
00047 {
00048 public:
00049     RDFTransformFactoryImpl(QString url);
00050     virtual ~RDFTransformFactoryImpl();
00051     
00052     bool isRDF();
00053     bool isOK();
00054     QString getErrorString() const;
00055 
00056     std::vector<Transform> getTransforms(ProgressReporter *);
00057 
00058     static QString writeTransformToRDF(const Transform &, QString);
00059 
00060 protected:
00061     BasicStore *m_store;
00062     QString m_urlString;
00063     QString m_errorString;
00064     bool m_isRDF;
00065     bool setOutput(Transform &, QString);
00066     bool setParameters(Transform &, QString);
00067 };
00068 
00069 
00070 QString
00071 RDFTransformFactory::getKnownExtensions()
00072 {
00073     return "*.rdf *.n3 *.ttl";
00074 }
00075 
00076 RDFTransformFactory::RDFTransformFactory(QString url) :
00077     m_d(new RDFTransformFactoryImpl(url)) 
00078 {
00079 }
00080 
00081 RDFTransformFactory::~RDFTransformFactory()
00082 {
00083     delete m_d;
00084 }
00085 
00086 bool
00087 RDFTransformFactory::isRDF()
00088 {
00089     return m_d->isRDF();
00090 }
00091 
00092 bool
00093 RDFTransformFactory::isOK()
00094 {
00095     return m_d->isOK();
00096 }
00097 
00098 QString
00099 RDFTransformFactory::getErrorString() const
00100 {
00101     return m_d->getErrorString();
00102 }
00103 
00104 std::vector<Transform>
00105 RDFTransformFactory::getTransforms(ProgressReporter *r)
00106 {
00107     return m_d->getTransforms(r);
00108 }
00109 
00110 QString
00111 RDFTransformFactory::writeTransformToRDF(const Transform &t, QString f)
00112 {
00113     return RDFTransformFactoryImpl::writeTransformToRDF(t, f);
00114 }
00115 
00116 RDFTransformFactoryImpl::RDFTransformFactoryImpl(QString url) :
00117     m_store(new BasicStore),
00118     m_urlString(url),
00119     m_isRDF(false)
00120 {
00122     m_store->addPrefix("vamp", Uri("http://purl.org/ontology/vamp/"));
00123     try {
00124         QUrl qurl;
00125         if (url.startsWith("file:")) {
00126             qurl = QUrl(url);
00127         } else {
00128             qurl = QUrl::fromLocalFile(url);
00129         }
00130         m_store->import(qurl, BasicStore::ImportIgnoreDuplicates);
00131         m_isRDF = true;
00132     } catch (...) { }
00133 }
00134 
00135 RDFTransformFactoryImpl::~RDFTransformFactoryImpl()
00136 {
00137     delete m_store;
00138 }
00139 
00140 bool
00141 RDFTransformFactoryImpl::isRDF()
00142 {
00143     return m_isRDF;
00144 }
00145 
00146 bool
00147 RDFTransformFactoryImpl::isOK()
00148 {
00149     return (m_errorString == "");
00150 }
00151 
00152 QString
00153 RDFTransformFactoryImpl::getErrorString() const
00154 {
00155     return m_errorString;
00156 }
00157 
00158 std::vector<Transform>
00159 RDFTransformFactoryImpl::getTransforms(ProgressReporter *)
00160 {
00161     std::vector<Transform> transforms;
00162 
00163     std::map<QString, Transform> uriTransformMap;
00164 
00165     Nodes tnodes = m_store->match
00166         (Triple(Node(), Uri("a"), m_store->expand("vamp:Transform"))).subjects();
00167 
00168     PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
00169 
00170     foreach (Node tnode, tnodes) {
00171 
00172         Node pnode = m_store->complete
00173             (Triple(tnode, m_store->expand("vamp:plugin"), Node()));
00174 
00175         if (pnode == Node()) {
00176             cerr << "RDFTransformFactory: WARNING: No vamp:plugin for "
00177                  << "vamp:Transform node " << tnode
00178                  << ", skipping this transform" << endl;
00179             continue;
00180         }
00181 
00182         QString transformUri = tnode.value;
00183         QString pluginUri = pnode.value;
00184 
00185         QString pluginId = indexer->getIdForPluginURI(pluginUri);
00186         if (pluginId == "") {
00187             cerr << "RDFTransformFactory: WARNING: Unknown plugin <"
00188                  << pluginUri << "> for transform <"
00189                  << transformUri << ">, skipping this transform"
00190                  << endl;
00191             continue;
00192         }
00193 
00194         Transform transform;
00195         transform.setPluginIdentifier(pluginId);
00196 
00197         if (!setOutput(transform, transformUri)) {
00198             return transforms;
00199         }
00200 
00201         if (!setParameters(transform, transformUri)) {
00202             return transforms;
00203         }
00204 
00205         uriTransformMap[transformUri] = transform;
00206 
00207         static const char *optionals[] = {
00208             "program",
00209             "summary_type",
00210             "step_size",
00211             "block_size",
00212             "window_type",
00213             "sample_rate",
00214             "start", 
00215             "duration"
00216         };
00217         
00218         for (int j = 0; j < int(sizeof(optionals)/sizeof(optionals[0])); ++j) {
00219 
00220             QString optional = optionals[j];
00221 
00222             Node onode = m_store->complete
00223                 (Triple(Uri(transformUri),
00224                         m_store->expand(QString("vamp:") + optional), Node()));
00225 
00226             if (onode.type != Node::Literal) continue;
00227 
00228             if (optional == "program") {
00229                 transform.setProgram(onode.value);
00230             } else if (optional == "summary_type") {
00231                 transform.setSummaryType
00232                     (transform.stringToSummaryType(onode.value));
00233             } else if (optional == "step_size") {
00234                 transform.setStepSize(onode.value.toUInt());
00235             } else if (optional == "block_size") {
00236                 transform.setBlockSize(onode.value.toUInt());
00237             } else if (optional == "window_type") {
00238                 transform.setWindowType
00239                     (Window<float>::getTypeForName
00240                      (onode.value.toLower().toStdString()));
00241             } else if (optional == "sample_rate") {
00242                 transform.setSampleRate(onode.value.toFloat());
00243             } else if (optional == "start") {
00244                 transform.setStartTime
00245                     (RealTime::fromXsdDuration(onode.value.toStdString()));
00246             } else if (optional == "duration") {
00247                 transform.setDuration
00248                     (RealTime::fromXsdDuration(onode.value.toStdString()));
00249             } else {
00250                 cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional << "\"" << endl;
00251             }
00252         }
00253 
00254         cerr << "RDFTransformFactory: NOTE: Transform is: " << endl;
00255         cerr << transform.toXmlString() << endl;
00256 
00257         transforms.push_back(transform);
00258     }
00259         
00260     return transforms;
00261 }
00262 
00263 bool
00264 RDFTransformFactoryImpl::setOutput(Transform &transform,
00265                                    QString transformUri)
00266 {
00267     Node outputNode = m_store->complete
00268         (Triple(Uri(transformUri), m_store->expand("vamp:output"), Node()));
00269     
00270     if (outputNode == Node()) return true;
00271 
00272     if (outputNode.type != Node::URI && outputNode.type != Node::Blank) {
00273         m_errorString = QString("vamp:output for output of transform <%1> is not a URI or blank node").arg(transformUri);
00274         return false;
00275     }
00276 
00277     // Now, outputNode might be the subject of a triple within m_store
00278     // that tells us the vamp:identifier, or it might be the subject
00279     // of a triple within the indexer that tells us it
00280 
00281     Node identNode = m_store->complete
00282         (Triple(outputNode, m_store->expand("vamp:identifier"), Node()));
00283 
00284     if (identNode == Node()) {
00285         PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
00286         const BasicStore *index = indexer->getIndex();
00287         identNode = index->complete
00288             (Triple(outputNode, index->expand("vamp:identifier"), Node()));
00289     }
00290 
00291     if (identNode == Node() || identNode.type != Node::Literal) {
00292         m_errorString = QString("No vamp:identifier found for output of transform <%1>, or vamp:identifier is not a literal").arg(transformUri);
00293         return false;
00294     }
00295 
00296     transform.setOutput(identNode.value);
00297 
00298     return true;
00299 }
00300         
00301 
00302 bool
00303 RDFTransformFactoryImpl::setParameters(Transform &transform,
00304                                        QString transformUri)
00305 {
00306     Nodes bindings = m_store->match
00307         (Triple(Uri(transformUri), m_store->expand("vamp:parameter_binding"), Node())).objects();
00308     
00309     foreach (Node binding, bindings) {
00310 
00311         Node paramNode = m_store->complete
00312             (Triple(binding, m_store->expand("vamp:parameter"), Node()));
00313 
00314         if (paramNode == Node()) {
00315             cerr << "RDFTransformFactoryImpl::setParameters: No vamp:parameter for binding " << binding << endl;
00316             continue;
00317         }
00318 
00319         Node valueNode = m_store->complete
00320             (Triple(binding, m_store->expand("vamp:value"), Node()));
00321 
00322         if (paramNode == Node()) {
00323             cerr << "RDFTransformFactoryImpl::setParameters: No vamp:value for binding " << binding << endl;
00324             continue;
00325         }
00326         
00327         // As with output above, paramNode might be the subject of a
00328         // triple within m_store that tells us the vamp:identifier, or
00329         // it might be the subject of a triple within the indexer that
00330         // tells us it
00331 
00332         Node idNode = m_store->complete
00333             (Triple(paramNode, m_store->expand("vamp:identifier"), Node()));
00334 
00335         if (idNode == Node()) {
00336             PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance();
00337             const BasicStore *index = indexer->getIndex();
00338             idNode = index->complete
00339                 (Triple(paramNode, index->expand("vamp:identifier"), Node()));
00340         }
00341 
00342         if (idNode == Node() || idNode.type != Node::Literal) {
00343             cerr << "RDFTransformFactoryImpl::setParameters: No vamp:identifier for parameter " << paramNode << endl;
00344             continue;
00345         }
00346         
00347         transform.setParameter(idNode.value, valueNode.value.toFloat());
00348     }
00349 
00350     return true;
00351 }
00352 
00353 QString
00354 RDFTransformFactoryImpl::writeTransformToRDF(const Transform &transform,
00355                                              QString uri)
00356 {
00357     QString str;
00358     QTextStream s(&str);
00359 
00360     // assumes the usual prefixes are available; requires that uri be
00361     // a local fragment (e.g. ":transform") rather than a uri enclosed
00362     // in <>, so that we can suffix it if need be
00363 
00364     QString pluginId = transform.getPluginIdentifier();
00365     QString pluginUri = PluginRDFIndexer::getInstance()->getURIForPluginId(pluginId);
00366 
00367     if (pluginUri != "") {
00368         s << uri << " a vamp:Transform ;" << endl;
00369         s << "    vamp:plugin <" << QUrl(pluginUri).toEncoded().data() << "> ;" << endl;
00370     } else {
00371         cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No plugin URI available for plugin id \"" << pluginId << "\", writing synthetic plugin and library resources" << endl;
00372         QString type, soname, label;
00373         PluginIdentifier::parseIdentifier(pluginId, type, soname, label);
00374         s << uri << "_plugin a vamp:Plugin ;" << endl;
00375         s << "    vamp:identifier \"" << label << "\" .\n" << endl;
00376         s << uri << "_library a vamp:PluginLibrary ;" << endl;
00377         s << "    vamp:identifier \"" << soname << "\" ;" << endl;
00378         s << "    vamp:available_plugin " << uri << "_plugin .\n" << endl;
00379         s << uri << " a vamp:Transform ;" << endl;
00380         s << "    vamp:plugin " << uri << "_plugin ;" << endl;
00381     }
00382 
00383     PluginRDFDescription description(pluginId);
00384     QString outputId = transform.getOutput();
00385     QString outputUri = description.getOutputUri(outputId);
00386 
00387     if (transform.getOutput() != "" && outputUri == "") {
00388         cerr << "WARNING: RDFTransformFactory::writeTransformToRDF: No output URI available for transform output id \"" << transform.getOutput() << "\", writing a synthetic output resource" << endl;
00389     }
00390 
00391     if (transform.getStepSize() != 0) {
00392         s << "    vamp:step_size \"" << transform.getStepSize() << "\"^^xsd:int ; " << endl;
00393     }
00394     if (transform.getBlockSize() != 0) {
00395         s << "    vamp:block_size \"" << transform.getBlockSize() << "\"^^xsd:int ; " << endl;
00396     }
00397     if (transform.getStartTime() != RealTime::zeroTime) {
00398         s << "    vamp:start \"" << transform.getStartTime().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
00399     }
00400     if (transform.getDuration() != RealTime::zeroTime) {
00401         s << "    vamp:duration \"" << transform.getDuration().toXsdDuration().c_str() << "\"^^xsd:duration ; " << endl;
00402     }
00403     if (transform.getSampleRate() != 0) {
00404         s << "    vamp:sample_rate \"" << transform.getSampleRate() << "\"^^xsd:float ; " << endl;
00405     }
00406     
00407     QString program = transform.getProgram();
00408     if (program != "") {
00409         s << "    vamp:program \"\"\"" << program << "\"\"\" ;" << endl;
00410     }
00411 
00412     QString summary = transform.summaryTypeToString(transform.getSummaryType());
00413     if (summary != "") {
00414         s << "    vamp:summary_type \"" << summary << "\" ;" << endl;
00415     }
00416 
00417     Transform::ParameterMap parameters = transform.getParameters();
00418     for (Transform::ParameterMap::const_iterator i = parameters.begin();
00419          i != parameters.end(); ++i) {
00420         QString name = i->first;
00421         float value = i->second;
00422         s << "    vamp:parameter_binding [" << endl;
00423         s << "        vamp:parameter [ vamp:identifier \"" << name << "\" ] ;" << endl;
00424         s << "        vamp:value \"" << value << "\"^^xsd:float ;" << endl;
00425         s << "    ] ;" << endl;
00426     }
00427 
00428     if (outputUri != "") {
00429         s << "    vamp:output <" << QUrl(outputUri).toEncoded().data() << "> ." << endl;
00430     } else if (outputId != "") {
00431         s << "    vamp:output [ vamp:identifier \"" << outputId << "\" ] ." << endl;
00432     } else {
00433         s << "    ." << endl;
00434     }
00435 
00436     return str;
00437 }
00438