M CHANGELOG +5 -0
@@ 1,4 1,9 @@
+Changes in Dataquay 0.9.5 since the previous version 0.9.1:
+
+ * Add importString methods
+
+
Changes in Dataquay 0.9.1 since the previous version 0.9:
* Update to support Qt5 and newer C++ compilers. Now requires C++11
M README.txt +8 -9
@@ 7,17 7,16 @@ API for an RDF data store using Qt class
http://breakfastquay.com/dataquay/
-This is version 0.9.1 of Dataquay. Note that this is a pre-1.0
+This is version 0.9.5 of Dataquay. Note that this is a pre-1.0
release and the API is still subject to change.
-Dataquay is simple to use and easy to integrate. It is principally
-intended for use in Qt-based applications that would like to use an
-RDF datastore as backing for in-memory project data, to avoid having
-to provide application data-specific file formats and to make it easy
-to augment the data with descriptive metadata pulled in from external
-sources. Dataquay is also intended to be useful for applications whose
-primary purpose is not related to RDF but that have ad-hoc RDF needs
-for metadata management.
+Dataquay is principally intended for use in Qt-based applications that
+would like to use an RDF datastore as backing for in-memory project
+data, to avoid having to provide application data-specific file
+formats and to make it easy to augment the data with descriptive
+metadata pulled in from external sources. Dataquay is also intended to
+be useful for applications whose primary purpose is not related to RDF
+but that have ad-hoc RDF needs for metadata management.
Dataquay does not include the datastore implementation itself; instead
it is a wrapper around either Redland (http://librdf.org) or Sord
M dataquay/BasicStore.h +18 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_BASIC_STORE_H_
-#define _DATAQUAY_BASIC_STORE_H_
+#ifndef DATAQUAY_BASIC_STORE_H
+#define DATAQUAY_BASIC_STORE_H
#include "Store.h"
@@ 117,6 117,8 @@ public:
void save(QString filename) const;
void import(QUrl url, ImportDuplicatesMode idm, QString format = "");
+ void importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format = "");
Features getSupportedFeatures() const;
@@ 138,6 140,20 @@ public:
*/
static BasicStore *load(QUrl url, QString format = "");
+ /**
+ * Construct a new BasicStore from the RDF document encoded in the
+ * given string. May throw RDFException. The returned BasicStore
+ * is owned by the caller and must be deleted using delete when
+ * finished with. The return value is never NULL; all errors
+ * result in exceptions.
+ *
+ * If format is specified, it will be taken as the RDF parse
+ * format (e.g. ntriples). The set of supported format strings
+ * depends on the underlying RDF library configuration. The
+ * default is to guess the format if possible.
+ */
+ static BasicStore *loadString(QString encodedRdf, Uri baseUri, QString format = "");
+
private:
class D;
D *m_d;
M dataquay/Connection.h +4 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_CONNECTION_H_
-#define _DATAQUAY_CONNECTION_H_
+#ifndef DATAQUAY_CONNECTION_H
+#define DATAQUAY_CONNECTION_H
#include "Store.h"
@@ 106,6 106,8 @@ public:
Uri expand(QString uri) const;
void save(QString filename) const;
void import(QUrl url, ImportDuplicatesMode idm, QString format = "");
+ void importString(QString encodedRdf, Uri uri,
+ ImportDuplicatesMode idm, QString format = "");
Features getSupportedFeatures() const;
public slots:
M dataquay/Node.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_NODE_H_
-#define _DATAQUAY_NODE_H_
+#ifndef DATAQUAY_NODE_H
+#define DATAQUAY_NODE_H
namespace Dataquay {
class Node;
M dataquay/PropertyObject.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_PROPERTY_OBJECT_H_
-#define _DATAQUAY_PROPERTY_OBJECT_H_
+#ifndef DATAQUAY_PROPERTY_OBJECT_H
+#define DATAQUAY_PROPERTY_OBJECT_H
#include <QString>
#include <QStringList>
M dataquay/RDFException.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_EXCEPTION_H_
-#define _DATAQUAY_EXCEPTION_H_
+#ifndef DATAQUAY_EXCEPTION_H
+#define DATAQUAY_EXCEPTION_H
#include <QString>
#include <exception>
M dataquay/Store.h +18 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_STORE_H_
-#define _DATAQUAY_STORE_H_
+#ifndef DATAQUAY_STORE_H
+#define DATAQUAY_STORE_H
#include "Triple.h"
@@ 244,6 244,22 @@ public:
virtual void import(QUrl url, ImportDuplicatesMode idm, QString format = "") = 0;
/**
+ * Import the RDF document encoded in the given string into the
+ * current store (in addition to its existing contents). Its
+ * behaviour when a triple is encountered that already exists in
+ * the store is controlled by the ImportDuplicatesMode.
+ *
+ * May throw RDFException or RDFDuplicateImportException.
+ *
+ * If format is specified, it will be taken as the RDF parse
+ * format (e.g. ntriples). The set of supported format strings
+ * depends on the underlying RDF library configuration. The
+ * default is to guess the format if possible.
+ */
+ virtual void importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format = "") = 0;
+
+ /**
* Feature defines the set of optional features a Store
* implementation may support. Code that uses Store should check
* that the features it requires are available before trying to
M dataquay/Transaction.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_TRANSACTION_H_
-#define _DATAQUAY_TRANSACTION_H_
+#ifndef DATAQUAY_TRANSACTION_H
+#define DATAQUAY_TRANSACTION_H
#include "Store.h"
M dataquay/TransactionalStore.h +6 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_TRANSACTIONAL_STORE_H_
-#define _DATAQUAY_TRANSACTIONAL_STORE_H_
+#ifndef DATAQUAY_TRANSACTIONAL_STORE_H
+#define DATAQUAY_TRANSACTIONAL_STORE_H
#include "Transaction.h"
@@ 136,6 136,8 @@ public:
Uri expand(QString uri) const;
void save(QString filename) const;
void import(QUrl url, ImportDuplicatesMode idm, QString format = "");
+ void importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format = "");
Features getSupportedFeatures() const;
signals:
@@ 180,6 182,8 @@ private:
Uri expand(QString uri) const;
void save(QString filename) const;
void import(QUrl url, ImportDuplicatesMode idm, QString format = "");
+ void importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format = "");
Features getSupportedFeatures() const;
// Transaction interface
M dataquay/Triple.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_TRIPLE_H_
-#define _DATAQUAY_TRIPLE_H_
+#ifndef DATAQUAY_TRIPLE_H
+#define DATAQUAY_TRIPLE_H
#include "Node.h"
M dataquay/Uri.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_URI_H_
-#define _DATAQUAY_URI_H_
+#ifndef DATAQUAY_URI_H
+#define DATAQUAY_URI_H
namespace Dataquay {
class Uri;
M dataquay/objectmapper/ContainerBuilder.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_CONTAINER_BUILDER_H_
-#define _DATAQUAY_CONTAINER_BUILDER_H_
+#ifndef DATAQUAY_CONTAINER_BUILDER_H
+#define DATAQUAY_CONTAINER_BUILDER_H
#include <QHash>
#include <QString>
M dataquay/objectmapper/ObjectBuilder.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_OBJECT_BUILDER_H_
-#define _DATAQUAY_OBJECT_BUILDER_H_
+#ifndef DATAQUAY_OBJECT_BUILDER_H
+#define DATAQUAY_OBJECT_BUILDER_H
#include <QHash>
#include <QString>
M dataquay/objectmapper/ObjectLoader.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_OBJECT_LOADER_H_
-#define _DATAQUAY_OBJECT_LOADER_H_
+#ifndef DATAQUAY_OBJECT_LOADER_H
+#define DATAQUAY_OBJECT_LOADER_H
#include "../Node.h"
M dataquay/objectmapper/ObjectMapper.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_OBJECT_MAPPER_H_
-#define _DATAQUAY_OBJECT_MAPPER_H_
+#ifndef DATAQUAY_OBJECT_MAPPER_H
+#define DATAQUAY_OBJECT_MAPPER_H
#include "../Node.h"
#include "../Store.h"
M dataquay/objectmapper/ObjectMapperDefs.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_OBJECT_MAPPER_DEFS_H_
-#define _DATAQUAY_OBJECT_MAPPER_DEFS_H_
+#ifndef DATAQUAY_OBJECT_MAPPER_DEFS_H
+#define DATAQUAY_OBJECT_MAPPER_DEFS_H
namespace Dataquay
{
M dataquay/objectmapper/ObjectMapperExceptions.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_OBJECT_MAPPER_EXCEPTIONS_H_
-#define _DATAQUAY_OBJECT_MAPPER_EXCEPTIONS_H_
+#ifndef DATAQUAY_OBJECT_MAPPER_EXCEPTIONS_H
+#define DATAQUAY_OBJECT_MAPPER_EXCEPTIONS_H
#include <exception>
#include <QString>
M dataquay/objectmapper/ObjectMapperForwarder.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_OBJECT_MAPPER_FORWARDER_H_
-#define _DATAQUAY_OBJECT_MAPPER_FORWARDER_H_
+#ifndef DATAQUAY_OBJECT_MAPPER_FORWARDER_H
+#define DATAQUAY_OBJECT_MAPPER_FORWARDER_H
#include <QObject>
M dataquay/objectmapper/ObjectStorer.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_OBJECT_STORER_H_
-#define _DATAQUAY_OBJECT_STORER_H_
+#ifndef DATAQUAY_OBJECT_STORER_H
+#define DATAQUAY_OBJECT_STORER_H
#include "../Node.h"
M dataquay/objectmapper/TypeMapping.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_TYPE_MAPPING_H_
-#define _DATAQUAY_TYPE_MAPPING_H_
+#ifndef DATAQUAY_TYPE_MAPPING_H
+#define DATAQUAY_TYPE_MAPPING_H
#include "../Uri.h"
M lib.pro +1 -1
@@ 13,7 13,7 @@ exists(config.pri) {
include(./config.pri)
}
-VERSION=0.9.1
+VERSION=0.9.5
OBJECTS_DIR = o
MOC_DIR = o
M src/Connection.cpp +17 -0
@@ 60,6 60,8 @@ public:
Uri expand(QString uri) const;
void save(QString filename) const;
void import(QUrl url, ImportDuplicatesMode idm, QString format);
+ void importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format);
Features getSupportedFeatures() const;
void commit();
@@ 228,6 230,14 @@ Connection::D::import(QUrl url, ImportDu
m_tx->import(url, idm, format);
}
+void
+Connection::D::importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format)
+{
+ start();
+ m_tx->importString(encodedRdf, baseUri, idm, format);
+}
+
Connection::Features
Connection::D::getSupportedFeatures() const
{
@@ 339,6 349,13 @@ Connection::import(QUrl url, ImportDupli
return m_d->import(url, idm, format);
}
+void
+Connection::importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format)
+{
+ return m_d->importString(encodedRdf, baseUri, idm, format);
+}
+
Connection::Features
Connection::getSupportedFeatures() const
{
M src/Debug.h +2 -2
@@ 31,8 31,8 @@
authorization.
*/
-#ifndef _DATAQUAY_INTERNAL_DEBUG_H_
-#define _DATAQUAY_INTERNAL_DEBUG_H_
+#ifndef DATAQUAY_INTERNAL_DEBUG_H
+#define DATAQUAY_INTERNAL_DEBUG_H
#include <QTextStream>
M src/TransactionalStore.cpp +43 -6
@@ 213,12 213,7 @@ public:
Operation op(this, tx);
m_store->save(filename);
}
-/*!!!
- void import(Transaction *tx, QUrl url, ImportDuplicatesMode idm, QString format) {
- Operation op(this, tx);
- m_store->import(url, idm, format);
- }
-*/
+
Features getSupportedFeatures() const {
return m_store->getSupportedFeatures();
}
@@ 569,6 564,30 @@ public:
}
}
+ void importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format) {
+ check();
+ BasicStore *bs = 0;
+ try {
+ bs = BasicStore::loadString(encodedRdf, baseUri, format);
+ Triples ts = bs->match(Triple());
+ foreach (Triple t, ts) {
+ bool added = m_td->add(m_tx, t);
+ if (added) {
+ m_changes.push_back(Change(AddTriple, t));
+ } else if (idm == ImportFailOnDuplicates) {
+ throw RDFDuplicateImportException("Duplicate statement encountered on import in ImportFailOnDuplicates mode");
+ }
+ }
+ delete bs;
+ bs = 0;
+ } catch (const RDFException &) {
+ delete bs;
+ abandon();
+ throw;
+ }
+ }
+
Features getSupportedFeatures() const {
return m_td->getSupportedFeatures();
}
@@ 637,6 656,17 @@ TransactionalStore::import(QUrl url, Imp
return tx->import(url, idm, format);
}
+void
+TransactionalStore::importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format)
+{
+ if (!m_d->hasWrap()) {
+ throw RDFException("TransactionalStore::import() called without Transaction");
+ }
+ unique_ptr<Transaction> tx(startTransaction());
+ return tx->importString(encodedRdf, baseUri, idm, format);
+}
+
TransactionalStore::Features
TransactionalStore::getSupportedFeatures() const
{
@@ 850,6 880,13 @@ TransactionalStore::TSTransaction::impor
m_d->import(url, idm, format);
}
+void
+TransactionalStore::TSTransaction::importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format)
+{
+ m_d->importString(encodedRdf, baseUri, idm, format);
+}
+
TransactionalStore::TSTransaction::Features
TransactionalStore::TSTransaction::getSupportedFeatures() const
{
M src/backend/BasicStoreRedland.cpp +216 -80
@@ 347,7 347,9 @@ public:
QMutexLocker locker(&m_librdfLock);
librdf_node *node = librdf_new_node_from_blank_identifier(m_w.getWorld(), 0);
if (!node) throw RDFInternalError("Failed to create new blank node");
- return lrdfNodeToNode(node);
+ Node n = lrdfNodeToNode(node);
+ librdf_free_node(node);
+ return n;
}
void save(QString filename) const {
@@ 357,6 359,14 @@ public:
DQ_DEBUG << "BasicStore::save(" << filename << ")" << endl;
+ QFile f(filename);
+ if (!f.exists()) {
+ if (!f.open(QFile::WriteOnly)) {
+ throw RDFException("Failed to open file for writing", filename);
+ }
+ f.close();
+ }
+
librdf_uri *base_uri = uriToLrdfUri(m_baseUri);
librdf_serializer *s = librdf_new_serializer(m_w.getWorld(), "turtle", 0, 0);
if (!s) throw RDFInternalError("Failed to construct RDF serializer");
@@ 366,15 376,7 @@ public:
QByteArray b = i.key().toUtf8();
librdf_serializer_set_namespace(s, uriToLrdfUri(i.value()), b.data());
}
- librdf_serializer_set_namespace(s, uriToLrdfUri(m_baseUri), "");
-
- QFile f(filename);
- if (!f.exists()) {
- if (!f.open(QFile::WriteOnly)) {
- throw RDFException("Failed to open file for writing", filename);
- }
- f.close();
- }
+ librdf_serializer_set_namespace(s, base_uri, "");
QString tmpFilename = QString("%1.part").arg(filename);
QByteArray b = QFile::encodeName(tmpFilename);
@@ 382,12 384,14 @@ public:
if (librdf_serializer_serialize_model_to_file(s, lname, base_uri, m_model)) {
librdf_free_serializer(s);
+ librdf_free_uri(base_uri);
QFile::remove(tmpFilename);
throw RDFException("Failed to export RDF model to temporary file",
tmpFilename);
}
librdf_free_serializer(s);
+ librdf_free_uri(base_uri);
// New file is now completed; the following is scruffy, but
// that shouldn't really matter now
@@ 440,6 444,8 @@ public:
librdf_free_parser(parser);
DQ_DEBUG << "librdf_parser_parse_into_model failed" << endl;
DQ_DEBUG << "luri = " << (const char *)librdf_uri_as_string(luri) << ", base_uri = " << (const char *)librdf_uri_as_string(base_uri) << endl;
+ librdf_free_uri(luri);
+ librdf_free_uri(base_uri);
throw RDFException("Failed to import model from URL",
url.toString());
}
@@ 464,60 470,97 @@ public:
throw RDFInternalError("Failed to create import RDF data model");
}
- librdf_stream *stream = 0;
- librdf_statement *all = 0;
-
- try { // so as to free parser and im on exception
-
- //!!! This appears to be returning success even on a
- //!!! syntax error -- can this be correct?
+ try {
if (librdf_parser_parse_into_model(parser, luri, base_uri, im)) {
DQ_DEBUG << "librdf_parser_parse_into_model failed" << endl;
DQ_DEBUG << "luri = " << (const char *)librdf_uri_as_string(luri) << ", base_uri = " << (const char *)librdf_uri_as_string(base_uri) << endl;
throw RDFException("Failed to import model from URL",
url.toString());
}
- all = tripleToStatement(Triple());
+
+ importFromTemporaryModel(im, idm);
+
+ } catch (...) {
+ librdf_free_parser(parser);
+ librdf_free_model(im);
+ librdf_free_storage(is);
+ librdf_free_uri(luri);
+ librdf_free_uri(base_uri);
+ throw;
+ }
+
+ librdf_free_model(im);
+ librdf_free_storage(is);
+ }
+
+ importNamespacesFromParser(parser);
+ librdf_free_parser(parser);
+ librdf_free_uri(luri);
+ librdf_free_uri(base_uri);
+ }
+
+ void importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format) {
+
+ QMutexLocker wlocker(&m_librdfLock);
+ QMutexLocker plocker(&m_prefixLock);
+
+ QString base = baseUri.toString();
+ librdf_uri *base_uri = uriToLrdfUri(Uri(base));
+
+ if (format == "") format = "guess";
+
+ librdf_parser *parser = librdf_new_parser
+ (m_w.getWorld(), format.toLocal8Bit().data(), NULL, NULL);
+ if (!parser) {
+ throw RDFInternalError("Failed to construct RDF parser");
+ }
- if (idm == ImportFailOnDuplicates) {
- // Need to query twice, first time to check for dupes
- stream = librdf_model_find_statements(im, all);
- if (!stream) {
- throw RDFInternalError("Failed to list imported RDF model in duplicates check");
- }
- while (!librdf_stream_end(stream)) {
- librdf_statement *current = librdf_stream_get_object(stream);
- if (!current) continue;
- if (librdf_model_contains_statement(m_model, current)) {
- throw RDFDuplicateImportException("Duplicate statement encountered on import in ImportFailOnDuplicates mode");
- }
- librdf_stream_next(stream);
- }
- librdf_free_stream(stream);
- stream = 0;
+ QByteArray rdfUtf8 = encodedRdf.toUtf8();
+
+ if (idm == ImportPermitDuplicates) {
+ // The normal Redland behaviour, so the easy case.
+ if (librdf_parser_parse_string_into_model
+ (parser, (const unsigned char *)rdfUtf8.data(),
+ base_uri, m_model)) {
+ librdf_free_parser(parser);
+ DQ_DEBUG << "librdf_parser_parse_into_model failed" << endl;
+ DQ_DEBUG << "base_uri = " << (const char *)librdf_uri_as_string(base_uri) << endl;
+ throw RDFException("Failed to import model from string");
+ }
+ } else { // ImportFailOnDuplicates and ImportIgnoreDuplicates modes
+
+ // This is complicated by our desire to avoid storing any
+ // duplicate triples on import, and optionally to be able
+ // to fail if any are found. So we import into a separate
+ // model and then transfer over. Not very efficient, but
+ // scalability is not generally the primary concern for us
+
+ librdf_storage *is = librdf_new_storage(m_w.getWorld(), "trees", 0, 0);
+ if (!is) is = librdf_new_storage(m_w.getWorld(), 0, 0, 0);
+ if (!is) {
+ librdf_free_parser(parser);
+ throw RDFInternalError("Failed to create import RDF data storage");
+ }
+ librdf_model *im = librdf_new_model(m_w.getWorld(), is, 0);
+ if (!im) {
+ librdf_free_storage(is);
+ librdf_free_parser(parser);
+ throw RDFInternalError("Failed to create import RDF data model");
+ }
+
+ try {
+ if (librdf_parser_parse_string_into_model
+ (parser, (const unsigned char *)rdfUtf8.data(),
+ base_uri, im)) {
+ DQ_DEBUG << "librdf_parser_parse_into_model failed" << endl;
+ DQ_DEBUG << "base_uri = " << (const char *)librdf_uri_as_string(base_uri) << endl;
+ throw RDFException("Failed to import model from URL");
}
- // Now import. Have to do this "manually" because librdf
- // may allow duplicates and we want to avoid them
- stream = librdf_model_find_statements(im, all);
- if (!stream) {
- throw RDFInternalError("Failed to list imported RDF model");
- }
- while (!librdf_stream_end(stream)) {
- librdf_statement *current = librdf_stream_get_object(stream);
- if (!current) continue;
- if (idm == ImportFailOnDuplicates || // (already tested if so)
- !librdf_model_contains_statement(m_model, current)) {
- librdf_model_add_statement(m_model, current);
- }
- librdf_stream_next(stream);
- }
- librdf_free_stream(stream);
- stream = 0;
+ importFromTemporaryModel(im, idm);
} catch (...) {
- if (stream) librdf_free_stream(stream);
- if (all) librdf_free_statement(all);
librdf_free_parser(parser);
librdf_free_model(im);
librdf_free_storage(is);
@@ 528,28 571,7 @@ public:
librdf_free_storage(is);
}
- int namespaces = librdf_parser_get_namespaces_seen_count(parser);
- DQ_DEBUG << "Parser found " << namespaces << " namespaces" << endl;
- for (int i = 0; i < namespaces; ++i) {
- const char *pfx = librdf_parser_get_namespaces_seen_prefix(parser, i);
- librdf_uri *uri = librdf_parser_get_namespaces_seen_uri(parser, i);
- QString qpfx = QString::fromUtf8(pfx);
- Uri quri;
- try {
- quri = lrdfUriToUri(uri);
- } catch (const RDFIncompleteURI &) {
- continue;
- }
- DQ_DEBUG << "namespace " << i << ": " << qpfx << " -> " << quri << endl;
- // don't call addPrefix; it tries to lock the mutex,
- // and anyway we want to add the prefix only if it
- // isn't already there (to avoid surprisingly changing
- // a prefix in unusual cases, or changing the base URI)
- if (m_prefixes.find(qpfx) == m_prefixes.end()) {
- m_prefixes[qpfx] = quri;
- }
- }
-
+ importNamespacesFromParser(parser);
librdf_free_parser(parser);
}
@@ 594,6 616,84 @@ private:
mutable int m_counter;
+ void importFromTemporaryModel(librdf_model *im, ImportDuplicatesMode idm) {
+
+ librdf_stream *stream = 0;
+ librdf_statement *all = 0;
+
+ try {
+
+ all = tripleToStatement(Triple());
+
+ if (idm == ImportFailOnDuplicates) {
+ // Need to query twice, first time to check for dupes
+ stream = librdf_model_find_statements(im, all);
+ if (!stream) {
+ throw RDFInternalError("Failed to list imported RDF model in duplicates check");
+ }
+ while (!librdf_stream_end(stream)) {
+ librdf_statement *current = librdf_stream_get_object(stream);
+ if (!current) continue;
+ if (librdf_model_contains_statement(m_model, current)) {
+ throw RDFDuplicateImportException("Duplicate statement encountered on import in ImportFailOnDuplicates mode");
+ }
+ librdf_stream_next(stream);
+ }
+ librdf_free_stream(stream);
+ stream = 0;
+ }
+
+ // Now import. Have to do this "manually" because librdf
+ // may allow duplicates and we want to avoid them
+ stream = librdf_model_find_statements(im, all);
+ if (!stream) {
+ throw RDFInternalError("Failed to list imported RDF model");
+ }
+ while (!librdf_stream_end(stream)) {
+ librdf_statement *current = librdf_stream_get_object(stream);
+ if (!current) continue;
+ if (idm == ImportFailOnDuplicates || // (already tested if so)
+ !librdf_model_contains_statement(m_model, current)) {
+ librdf_model_add_statement(m_model, current);
+ }
+ librdf_stream_next(stream);
+ }
+
+ } catch (...) {
+ if (stream) librdf_free_stream(stream);
+ if (all) librdf_free_statement(all);
+ throw;
+ }
+
+ if (stream) librdf_free_stream(stream);
+ if (all) librdf_free_statement(all);
+ }
+
+ void importNamespacesFromParser(librdf_parser *parser) {
+
+ int namespaces = librdf_parser_get_namespaces_seen_count(parser);
+ DQ_DEBUG << "Parser found " << namespaces << " namespaces" << endl;
+ for (int i = 0; i < namespaces; ++i) {
+ const char *pfx = librdf_parser_get_namespaces_seen_prefix(parser, i);
+ librdf_uri *uri = librdf_parser_get_namespaces_seen_uri(parser, i);
+ QString qpfx = QString::fromUtf8(pfx);
+ Uri quri;
+ try {
+ quri = lrdfUriToUri(uri);
+ } catch (const RDFIncompleteURI &) {
+ continue;
+ }
+ DQ_DEBUG << "namespace " << i << ": " << qpfx << " -> " << quri << endl;
+ // don't call addPrefix; it tries to lock the mutex,
+ // and anyway we want to add the prefix only if it
+ // isn't already there (to avoid surprisingly changing
+ // a prefix in unusual cases, or changing the base URI)
+ if (m_prefixes.find(qpfx) == m_prefixes.end()) {
+ m_prefixes[qpfx] = quri;
+ }
+ }
+ }
+
bool doAdd(Triple t) {
librdf_statement *statement = tripleToStatement(t);
if (!checkComplete(statement)) {
@@ 736,11 836,24 @@ private:
lrdfNodeToNode(object));
return triple;
}
-
+
bool checkComplete(librdf_statement *statement) const {
if (librdf_statement_is_complete(statement)) return true;
else {
- unsigned char *text = librdf_statement_to_string(statement);
+ unsigned char *text = nullptr;
+ raptor_iostream *iostr = raptor_new_iostream_to_string
+ (librdf_world_get_raptor(m_w.getWorld()),
+ (void **)&text, nullptr, malloc);
+ int rc = 1;
+ if (iostr) {
+ rc = librdf_statement_write(statement, iostr);
+ raptor_free_iostream(iostr);
+ }
+ if (rc) {
+ if (text) free(text);
+ std::cerr << "BasicStore::checkComplete: WARNING: RDF statement is incomplete, and writing it to a diagnostic string failed" << std::endl;
+ return false;
+ }
QString str = QString::fromUtf8((char *)text);
std::cerr << "BasicStore::checkComplete: WARNING: RDF statement is incomplete: " << str.toStdString() << std::endl;
free(text);
@@ 754,8 867,10 @@ private:
Triples results;
librdf_statement *templ = tripleToStatement(t);
librdf_stream *stream = librdf_model_find_statements(m_model, templ);
- librdf_free_statement(templ);
- if (!stream) throw RDFInternalError("Failed to match RDF triples");
+ if (!stream) {
+ librdf_free_statement(templ);
+ throw RDFInternalError("Failed to match RDF triples");
+ }
while (!librdf_stream_end(stream)) {
librdf_statement *current = librdf_stream_get_object(stream);
if (current) results.push_back(statementToTriple(current));
@@ 763,6 878,7 @@ private:
librdf_stream_next(stream);
}
librdf_free_stream(stream);
+ librdf_free_statement(templ);
return results;
}
@@ 808,7 924,10 @@ private:
librdf_node *node =
librdf_query_results_get_binding_value(results, i);
- dict[key] = lrdfNodeToNode(node);
+ if (node) {
+ dict[key] = lrdfNodeToNode(node);
+ librdf_free_node(node);
+ }
}
returned.push_back(dict);
@@ 960,6 1079,13 @@ BasicStore::import(QUrl url, ImportDupli
m_d->import(url, idm, format);
}
+void
+BasicStore::importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format)
+{
+ m_d->importString(encodedRdf, baseUri, idm, format);
+}
+
BasicStore *
BasicStore::load(QUrl url, QString format)
{
@@ 970,6 1096,16 @@ BasicStore::load(QUrl url, QString forma
return s;
}
+BasicStore *
+BasicStore::loadString(QString encodedRdf, Uri baseUri, QString format)
+{
+ BasicStore *s = new BasicStore();
+ s->setBaseUri(baseUri);
+ // store is empty, ImportIgnoreDuplicates is faster
+ s->importString(encodedRdf, baseUri, ImportIgnoreDuplicates, format);
+ return s;
+}
+
BasicStore::Features
BasicStore::getSupportedFeatures() const
{
M src/backend/BasicStoreSord.cpp +156 -32
@@ 504,7 504,7 @@ public:
}
SerdStatus rv = serd_reader_read_file
- (reader, (const uint8_t *)fileUri.toLocal8Bit().data());
+ (reader, (const uint8_t *)fileUri.toUtf8().data());
if (rv != SERD_SUCCESS) {
serd_reader_free(reader);
@@ 535,7 535,7 @@ public:
}
SerdStatus rv = serd_reader_read_file
- (reader, (const uint8_t *)fileUri.toLocal8Bit().data());
+ (reader, (const uint8_t *)fileUri.toUtf8().data());
if (rv != SERD_SUCCESS) {
serd_reader_free(reader);
@@ 549,40 549,111 @@ public:
serd_reader_free(reader);
- SordQuad templ;
- tripleToStatement(Triple(), templ);
+ try {
+ importFromTemporaryModel(im, idm);
+ } catch (...) {
+ sord_free(im);
+ serd_env_free(env);
+ throw;
+ }
+
+ sord_free(im);
+ }
+
+ serd_env_foreach(env, addPrefixSink, this);
+ serd_env_free(env);
+ }
+
+ void importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString /* format */) {
+
+ DQ_DEBUG << "BasicStoreSord::importString" << endl;
- if (idm == ImportFailOnDuplicates) {
+ QMutexLocker wlocker(&m_backendLock);
+ QMutexLocker plocker(&m_prefixLock);
+
+ //!!! todo: format?
+
+ QString base = baseUri.toString();
+ QByteArray bb = base.toUtf8();
+ SerdURI bu;
+
+ if (serd_uri_parse((uint8_t *)bb.data(), &bu) != SERD_SUCCESS) {
+ throw RDFInternalError("Failed to parse base URI", base);
+ }
+
+ SerdNode bn = serd_node_from_string(SERD_URI, (uint8_t *)bb.data());
+ SerdEnv *env = serd_env_new(&bn);
+
+ QByteArray rdfUtf8 = encodedRdf.toUtf8();
+
+ if (idm == ImportPermitDuplicates) {
+
+ // No special handling for duplicates, do whatever the
+ // underlying engine does
+
+ SerdReader *reader = sord_new_reader(m_model, env, SERD_TURTLE, NULL);
- SordIter *itr = sord_find(im, templ);
- while (!sord_iter_end(itr)) {
- SordQuad q;
- sord_iter_get(itr, q);
- if (sord_contains(m_model, q)) {
- Triple culprit = statementToTriple(q);
- sord_iter_free(itr);
- freeStatement(templ);
- sord_free(im);
- serd_env_free(env);
- throw RDFDuplicateImportException("Duplicate statement encountered on import in ImportFailOnDuplicates mode", culprit);
- }
- sord_iter_next(itr);
- }
- sord_iter_free(itr);
+ // if we have data in the store already, then we must add
+ // a prefix for the new blank nodes we're importing to
+ // disambiguate them
+ if (!doMatch(Triple(), true).empty()) {
+ serd_reader_add_blank_prefix
+ (reader, (uint8_t *)(getNewString().toUtf8().data()));
+ }
+
+ SerdStatus rv = serd_reader_read_string
+ (reader, (const uint8_t *)rdfUtf8.data());
+
+ if (rv != SERD_SUCCESS) {
+ serd_reader_free(reader);
+ serd_env_free(env);
+ throw RDFException
+ (QString("Failed to import model from string: %1")
+ .arg(serdStatusToString(rv)));
+ }
+
+ serd_reader_free(reader);
+
+ } else {
+
+ // ImportFailOnDuplicates and ImportIgnoreDuplicates:
+ // import into a separate model and transfer across
+
+ SordModel *im = sord_new(m_w.getWorld(), 0, false); // no index
+
+ SerdReader *reader = sord_new_reader(im, env, SERD_TURTLE, NULL);
+
+ // if we have data in the store already, then we must add
+ // a prefix for the new blank nodes we're importing to
+ // disambiguate them
+ if (!doMatch(Triple(), true).empty()) {
+ serd_reader_add_blank_prefix
+ (reader, (uint8_t *)(getNewString().toUtf8().data()));
+ }
+
+ SerdStatus rv = serd_reader_read_string
+ (reader, (const uint8_t *)rdfUtf8.data());
+
+ if (rv != SERD_SUCCESS) {
+ serd_reader_free(reader);
+ sord_free(im);
+ serd_env_free(env);
+ throw RDFException
+ (QString("Failed to import model from string: %1")
+ .arg(serdStatusToString(rv)));
+ }
+
+ serd_reader_free(reader);
+
+ try {
+ importFromTemporaryModel(im, idm);
+ } catch (...) {
+ sord_free(im);
+ serd_env_free(env);
+ throw;
}
- SordIter *itr = sord_find(im, templ);
- while (!sord_iter_end(itr)) {
- SordQuad q;
- sord_iter_get(itr, q);
- if (idm == ImportFailOnDuplicates || // (already tested if so)
- !sord_contains(m_model, q)) {
- sord_add(m_model, q);
- }
- sord_iter_next(itr);
- }
- sord_iter_free(itr);
- freeStatement(templ);
sord_free(im);
}
@@ 630,6 701,42 @@ private:
PrefixMap m_prefixes;
mutable QMutex m_prefixLock; // also protects m_baseUri
+ void importFromTemporaryModel(SordModel *im, ImportDuplicatesMode idm) {
+
+ SordQuad templ;
+ tripleToStatement(Triple(), templ);
+
+ if (idm == ImportFailOnDuplicates) {
+
+ SordIter *itr = sord_find(im, templ);
+ while (!sord_iter_end(itr)) {
+ SordQuad q;
+ sord_iter_get(itr, q);
+ if (sord_contains(m_model, q)) {
+ Triple culprit = statementToTriple(q);
+ sord_iter_free(itr);
+ freeStatement(templ);
+ throw RDFDuplicateImportException("Duplicate statement encountered on import in ImportFailOnDuplicates mode", culprit);
+ }
+ sord_iter_next(itr);
+ }
+ sord_iter_free(itr);
+ }
+
+ SordIter *itr = sord_find(im, templ);
+ while (!sord_iter_end(itr)) {
+ SordQuad q;
+ sord_iter_get(itr, q);
+ if (idm == ImportFailOnDuplicates || // (already tested if so)
+ !sord_contains(m_model, q)) {
+ sord_add(m_model, q);
+ }
+ sord_iter_next(itr);
+ }
+ sord_iter_free(itr);
+ freeStatement(templ);
+ }
+
bool doAdd(Triple t) {
SordQuad statement;
tripleToStatement(t, statement);
@@ 965,6 1072,13 @@ BasicStore::import(QUrl url, ImportDupli
m_d->import(url, idm, format);
}
+void
+BasicStore::importString(QString encodedRdf, Uri baseUri,
+ ImportDuplicatesMode idm, QString format)
+{
+ m_d->importString(encodedRdf, baseUri, idm, format);
+}
+
BasicStore *
BasicStore::load(QUrl url, QString format)
{
@@ 977,6 1091,16 @@ BasicStore::load(QUrl url, QString forma
return s;
}
+BasicStore *
+BasicStore::loadString(QString encodedRdf, Uri baseUri, QString format)
+{
+ BasicStore *s = new BasicStore();
+ s->setBaseUri(baseUri);
+ // store is empty, ImportIgnoreDuplicates is faster
+ s->importString(encodedRdf, baseUri, ImportIgnoreDuplicates, format);
+ return s;
+}
+
BasicStore::Features
BasicStore::getSupportedFeatures() const
{
M tests/TestBasicStore.h +4 -0
@@ 562,6 562,7 @@ private slots:
t = s2->matchOnce(Triple(Node(), Uri("a"), Node()));
QCOMPARE(t.a, Node(Uri("file://test3.ttl#thing")));
QCOMPARE(t.c, Node(Uri("file://test3.ttl#wotsit")));
+ delete s2;
}
void loadMultiBase() {
@@ 623,6 624,9 @@ private slots:
(Triple(Node(Uri("http://breakfastquay.com/rdf/dataquay/tests#fred")),
store.expand(":age"),
Node("42", store.expand("xsd:integer")))));
+
+ delete target;
+ delete otherStore;
}
void loadCompetingBlanks() {
M tests/TestTransactionalStore.h +6 -0
@@ 55,6 55,10 @@ private slots:
ts = new TransactionalStore(&store);
}
+ void cleanupTestCase() {
+ delete ts;
+ }
+
void init() {
store.clear();
}
@@ 275,6 279,8 @@ private slots:
cchanges = t->getCommittedChanges();
QCOMPARE(cchanges, changes);
+ delete t;
+
t = ts->startTransaction();
t->revert(changes);
t->commit();