Chris@439: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@439: Chris@439: /* Chris@439: Sonic Visualiser Chris@439: An audio file viewer and annotation editor. Chris@439: Centre for Digital Music, Queen Mary, University of London. Chris@439: This file copyright 2008 QMUL. Chris@439: Chris@439: This program is free software; you can redistribute it and/or Chris@439: modify it under the terms of the GNU General Public License as Chris@439: published by the Free Software Foundation; either version 2 of the Chris@439: License, or (at your option) any later version. See the file Chris@439: COPYING included with this distribution for more information. Chris@439: */ Chris@439: Chris@439: #include "RDFTransformFactory.h" Chris@439: Chris@439: #include Chris@439: #include Chris@439: Chris@439: #include Chris@439: #include Chris@439: Chris@439: #include "SimpleSPARQLQuery.h" Chris@439: #include "PluginRDFIndexer.h" Chris@439: #include "base/ProgressReporter.h" Chris@439: Chris@439: #include "transform/TransformFactory.h" Chris@439: Chris@439: using std::cerr; Chris@439: using std::endl; Chris@439: Chris@439: typedef const unsigned char *STR; // redland's expected string type Chris@439: Chris@439: Chris@439: class RDFTransformFactoryImpl Chris@439: { Chris@439: public: Chris@439: RDFTransformFactoryImpl(QString url); Chris@439: virtual ~RDFTransformFactoryImpl(); Chris@439: Chris@439: bool isOK(); Chris@439: QString getErrorString() const; Chris@439: Chris@439: std::vector getTransforms(ProgressReporter *); Chris@439: Chris@439: protected: Chris@439: QString m_urlString; Chris@439: QString m_errorString; Chris@440: bool setOutput(Transform &, QString, QString); Chris@440: bool setParameters(Transform &, QString, QString); Chris@439: }; Chris@439: Chris@439: Chris@439: QString Chris@439: RDFTransformFactory::getKnownExtensions() Chris@439: { Chris@439: return "*.rdf *.n3 *.ttl"; Chris@439: } Chris@439: Chris@439: RDFTransformFactory::RDFTransformFactory(QString url) : Chris@439: m_d(new RDFTransformFactoryImpl(url)) Chris@439: { Chris@439: } Chris@439: Chris@439: RDFTransformFactory::~RDFTransformFactory() Chris@439: { Chris@439: delete m_d; Chris@439: } Chris@439: Chris@439: bool Chris@439: RDFTransformFactory::isOK() Chris@439: { Chris@439: return m_d->isOK(); Chris@439: } Chris@439: Chris@439: QString Chris@439: RDFTransformFactory::getErrorString() const Chris@439: { Chris@439: return m_d->getErrorString(); Chris@439: } Chris@439: Chris@439: std::vector Chris@439: RDFTransformFactory::getTransforms(ProgressReporter *r) Chris@439: { Chris@439: return m_d->getTransforms(r); Chris@439: } Chris@439: Chris@439: RDFTransformFactoryImpl::RDFTransformFactoryImpl(QString url) : Chris@439: m_urlString(url) Chris@439: { Chris@439: } Chris@439: Chris@439: RDFTransformFactoryImpl::~RDFTransformFactoryImpl() Chris@439: { Chris@439: } Chris@439: Chris@439: bool Chris@439: RDFTransformFactoryImpl::isOK() Chris@439: { Chris@439: return (m_errorString == ""); Chris@439: } Chris@439: Chris@439: QString Chris@439: RDFTransformFactoryImpl::getErrorString() const Chris@439: { Chris@439: return m_errorString; Chris@439: } Chris@439: Chris@439: std::vector Chris@439: RDFTransformFactoryImpl::getTransforms(ProgressReporter *reporter) Chris@439: { Chris@439: std::vector transforms; Chris@439: Chris@440: // We have to do this a very long way round, to work around Chris@440: // rasqal's current inability to handle correctly more than one Chris@440: // OPTIONAL graph in a query Chris@439: Chris@440: const char *optionals[] = { Chris@440: "output", Chris@440: "program", Chris@440: "step_size", Chris@440: "block_size", Chris@440: "window_type", Chris@440: "sample_rate", Chris@440: "start", Chris@440: "duration" Chris@440: }; Chris@439: Chris@440: std::map uriTransformMap; Chris@439: Chris@440: QString queryTemplate = Chris@440: " PREFIX vamp: " Chris@439: Chris@440: " SELECT ?transform ?plugin %1 " Chris@440: Chris@440: " FROM <%2> " Chris@439: Chris@440: " WHERE { " Chris@440: " ?transform a vamp:Transform ; " Chris@440: " vamp:plugin ?plugin . " Chris@440: " %3 " Chris@440: " } "; Chris@440: Chris@440: SimpleSPARQLQuery transformsQuery Chris@440: (queryTemplate.arg("").arg(m_urlString).arg("")); Chris@440: Chris@440: SimpleSPARQLQuery::ResultList transformResults = transformsQuery.execute(); Chris@440: Chris@440: if (!transformsQuery.isOK()) { Chris@440: m_errorString = transformsQuery.getErrorString(); Chris@439: return transforms; Chris@439: } Chris@439: Chris@440: if (transformResults.empty()) { Chris@440: cerr << "RDFTransformFactory: NOTE: No RDF/TTL transform descriptions found in document at <" << m_urlString.toStdString() << ">" << endl; Chris@439: return transforms; Chris@439: } Chris@439: Chris@439: PluginRDFIndexer *indexer = PluginRDFIndexer::getInstance(); Chris@439: Chris@440: for (int i = 0; i < transformResults.size(); ++i) { Chris@439: Chris@440: SimpleSPARQLQuery::KeyValueMap &result = transformResults[i]; Chris@439: Chris@439: QString transformUri = result["transform"].value; Chris@439: QString pluginUri = result["plugin"].value; Chris@439: Chris@439: QString pluginId = indexer->getIdForPluginURI(pluginUri); Chris@439: if (pluginId == "") { Chris@439: cerr << "RDFTransformFactory: WARNING: Unknown plugin <" Chris@439: << pluginUri.toStdString() << "> for transform <" Chris@440: << transformUri.toStdString() << ">, skipping this transform" Chris@440: << endl; Chris@440: continue; Chris@440: } Chris@440: Chris@440: QString pluginDescriptionURL = Chris@440: indexer->getDescriptionURLForPluginId(pluginId); Chris@440: if (pluginDescriptionURL == "") { Chris@440: cerr << "RDFTransformFactory: WARNING: No RDF description available for plugin <" Chris@440: << pluginUri.toStdString() << ">, skipping transform <" Chris@439: << transformUri.toStdString() << ">" << endl; Chris@439: continue; Chris@439: } Chris@439: Chris@439: Transform transform; Chris@439: transform.setPluginIdentifier(pluginId); Chris@439: Chris@440: if (!setOutput(transform, transformUri, pluginDescriptionURL)) { Chris@439: return transforms; Chris@439: } Chris@439: Chris@440: if (!setParameters(transform, transformUri, pluginDescriptionURL)) { Chris@439: return transforms; Chris@439: } Chris@439: Chris@440: uriTransformMap[transformUri] = transform; Chris@440: } Chris@439: Chris@440: for (int i = 0; i < sizeof(optionals)/sizeof(optionals[0]); ++i) { Chris@439: Chris@440: QString optional = optionals[i]; Chris@439: Chris@440: SimpleSPARQLQuery query Chris@440: (queryTemplate Chris@440: .arg(QString("?%1").arg(optional)) Chris@440: .arg(m_urlString) Chris@440: .arg(QString("?transform vamp:%1 ?%2") Chris@440: .arg(optionals[i]).arg(optional))); Chris@440: Chris@440: SimpleSPARQLQuery::ResultList results = query.execute(); Chris@440: Chris@440: if (!query.isOK()) { Chris@440: m_errorString = query.getErrorString(); Chris@440: return transforms; Chris@439: } Chris@439: Chris@440: if (results.empty()) continue; Chris@440: Chris@440: for (int j = 0; j < results.size(); ++j) { Chris@440: Chris@440: QString transformUri = results[j]["transform"].value; Chris@440: Chris@440: if (uriTransformMap.find(transformUri) == uriTransformMap.end()) { Chris@440: cerr << "RDFTransformFactory: ERROR: Transform URI <" Chris@440: << transformUri.toStdString() << "> not found in internal map!" << endl; Chris@440: continue; Chris@440: } Chris@440: Chris@440: Transform &transform = uriTransformMap[transformUri]; Chris@440: const SimpleSPARQLQuery::Value &v = results[j][optional]; Chris@440: Chris@440: if (v.type == SimpleSPARQLQuery::LiteralValue) { Chris@440: Chris@440: if (optional == "program") { Chris@440: transform.setProgram(v.value); Chris@440: } else if (optional == "step_size") { Chris@440: transform.setStepSize(v.value.toUInt()); Chris@440: } else if (optional == "block_size") { Chris@440: transform.setBlockSize(v.value.toUInt()); Chris@440: } else if (optional == "window_type") { Chris@440: cerr << "NOTE: can't handle window type yet (value is \"" Chris@440: << v.value.toStdString() << "\")" << endl; Chris@440: } else if (optional == "sample_rate") { Chris@440: transform.setSampleRate(v.value.toFloat()); Chris@440: } else if (optional == "start") { Chris@440: transform.setStartTime Chris@440: (RealTime::fromXsdDuration(v.value.toStdString())); Chris@440: } else if (optional == "duration") { Chris@440: transform.setDuration Chris@440: (RealTime::fromXsdDuration(v.value.toStdString())); Chris@440: } else { Chris@440: cerr << "RDFTransformFactory: ERROR: Inconsistent optionals lists (unexpected optional \"" << optional.toStdString() << "\"" << endl; Chris@440: } Chris@440: } Chris@440: } Chris@440: } Chris@440: Chris@440: for (std::map::iterator i = uriTransformMap.begin(); Chris@440: i != uriTransformMap.end(); ++i) { Chris@440: Chris@440: Transform &transform = i->second; Chris@440: Chris@439: cerr << "RDFTransformFactory: NOTE: Transform is: " << endl; Chris@439: cerr << transform.toXmlString().toStdString() << endl; Chris@439: Chris@439: transforms.push_back(transform); Chris@439: } Chris@439: Chris@439: return transforms; Chris@439: } Chris@439: Chris@440: bool Chris@440: RDFTransformFactoryImpl::setOutput(Transform &transform, Chris@440: QString transformUri, Chris@440: QString pluginDescriptionURL) Chris@440: { Chris@440: SimpleSPARQLQuery outputQuery Chris@440: (QString Chris@440: ( Chris@440: " PREFIX vamp: " Chris@440: Chris@440: " SELECT ?output_id " Chris@440: Chris@440: " FROM <%1> " Chris@440: " FROM <%2> " Chris@440: Chris@440: " WHERE { " Chris@440: " <%3> vamp:output ?output . " Chris@440: " ?output vamp:identifier ?output_id " Chris@440: " } " Chris@440: ) Chris@440: .arg(m_urlString) Chris@440: .arg(pluginDescriptionURL) Chris@440: .arg(transformUri)); Chris@440: Chris@440: SimpleSPARQLQuery::ResultList outputResults = outputQuery.execute(); Chris@440: Chris@440: if (!outputQuery.isOK()) { Chris@440: m_errorString = outputQuery.getErrorString(); Chris@440: return false; Chris@440: } Chris@440: Chris@440: if (outputQuery.wasCancelled()) { Chris@440: m_errorString = "Query cancelled"; Chris@440: return false; Chris@440: } Chris@440: Chris@440: for (int j = 0; j < outputResults.size(); ++j) { Chris@440: QString outputId = outputResults[j]["output_id"].value; Chris@440: transform.setOutput(outputId); Chris@440: } Chris@440: Chris@440: return true; Chris@440: } Chris@440: Chris@440: Chris@440: bool Chris@440: RDFTransformFactoryImpl::setParameters(Transform &transform, Chris@440: QString transformUri, Chris@440: QString pluginDescriptionURL) Chris@440: { Chris@440: SimpleSPARQLQuery paramQuery Chris@440: (QString Chris@440: ( Chris@440: " PREFIX vamp: " Chris@440: Chris@440: " SELECT ?param_id ?param_value " Chris@440: Chris@440: " FROM <%1> " Chris@440: " FROM <%2> " Chris@440: Chris@440: " WHERE { " Chris@440: " <%3> vamp:parameter_binding ?binding . " Chris@440: " ?binding vamp:parameter ?param ; " Chris@440: " vamp:value ?param_value . " Chris@440: " ?param vamp:identifier ?param_id " Chris@440: " } " Chris@440: ) Chris@440: .arg(m_urlString) Chris@440: .arg(pluginDescriptionURL) Chris@440: .arg(transformUri)); Chris@440: Chris@440: SimpleSPARQLQuery::ResultList paramResults = paramQuery.execute(); Chris@440: Chris@440: if (!paramQuery.isOK()) { Chris@440: m_errorString = paramQuery.getErrorString(); Chris@440: return false; Chris@440: } Chris@440: Chris@440: if (paramQuery.wasCancelled()) { Chris@440: m_errorString = "Query cancelled"; Chris@440: return false; Chris@440: } Chris@440: Chris@440: for (int j = 0; j < paramResults.size(); ++j) { Chris@440: Chris@440: QString paramId = paramResults[j]["param_id"].value; Chris@440: QString paramValue = paramResults[j]["param_value"].value; Chris@440: Chris@440: if (paramId == "" || paramValue == "") continue; Chris@440: Chris@440: transform.setParameter(paramId, paramValue.toFloat()); Chris@440: } Chris@440: Chris@440: return true; Chris@440: } Chris@440: