From 0fb533abe4f1c9fdf5e9d7842f3a3f1cc9136e79 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Wed, 16 Feb 2011 18:08:45 +0000 Subject: Make serd.h C++ safe. Add C++ API (serdmm.hpp). Add serialisation API (easily write models to files). git-svn-id: http://svn.drobilla.net/sord/trunk@36 3d64ff67-21c5-427c-a301-fe4f08042e5a --- sord/sord.h | 27 +++ sord/sordmm.hpp | 504 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 531 insertions(+) create mode 100644 sord/sordmm.hpp (limited to 'sord') diff --git a/sord/sord.h b/sord/sord.h index 6b00268..f1169f9 100644 --- a/sord/sord.h +++ b/sord/sord.h @@ -22,9 +22,15 @@ #ifndef SORD_SORD_H #define SORD_SORD_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include +#include "serd/serd.h" + #if defined _WIN32 || defined __CYGWIN__ #define SORD_LIB_IMPORT __declspec(dllimport) #define SORD_LIB_EXPORT __declspec(dllexport) @@ -330,8 +336,29 @@ sord_read_file_handle(SordModel model, const SordNode graph, const uint8_t* blank_prefix); +SORD_API +bool +sord_write_file(SordModel model, + SerdEnv env, + const uint8_t* uri, + const SordNode graph, + const uint8_t* blank_prefix); + +SORD_API +bool +sord_write_file_handle(SordModel model, + SerdEnv env, + FILE* fd, + const uint8_t* base_uri, + const SordNode graph, + const uint8_t* blank_prefix); + /** @} */ /** @} */ +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif // SORD_SORD_H diff --git a/sord/sordmm.hpp b/sord/sordmm.hpp new file mode 100644 index 0000000..4661458 --- /dev/null +++ b/sord/sordmm.hpp @@ -0,0 +1,504 @@ +/* Sord, a lightweight RDF model library. + * Copyright 2010-2011 David Robillard + * + * Sord is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sord is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +/** @file sordmm.hpp + * Public Sord C++ API. + */ + +#ifndef SORD_SORDMM_HPP +#define SORD_SORDMM_HPP + +#include +#include +#include +#include +#include + +#include +#include + +#include "serd/serd.h" +#include "sord/sord.h" + +#define SORD_NS_XSD "http://www.w3.org/2001/XMLSchema#" + +namespace Sord { + +/** C++ wrapper for a Sord object. */ +template +class Wrapper { +public: + inline Wrapper(T c_obj = NULL) : _c_obj(c_obj) {} + + inline T c_obj() { return _c_obj; } + inline const T c_obj() const { return _c_obj; } + +protected: + T _c_obj; +}; + +/** Collection of RDF namespaces with prefixes. */ +class Namespaces : public Wrapper { +public: + Namespaces() : Wrapper(serd_env_new()) {} + + static inline SerdNode string_to_node(SerdType type, const std::string& s) { + SerdNode ret = { + type, s.length() + 1, s.length(), (const uint8_t*)s.c_str() }; + return ret; + } + + inline void add(const std::string& name, + const std::string& uri) { + const SerdNode name_node = string_to_node(SERD_LITERAL, name); + const SerdNode uri_node = string_to_node(SERD_URI, uri); + serd_env_add(_c_obj, &name_node, &uri_node); + } + + inline std::string qualify(std::string uri) const { + const SerdNode uri_node = string_to_node(SERD_URI, uri); + SerdNode prefix; + SerdChunk suffix; + if (serd_env_qualify(_c_obj, &uri_node, &prefix, &suffix)) { + std::string ret((const char*)prefix.buf, prefix.n_bytes - 1); + ret.append((const char*)suffix.buf, suffix.len); + return ret; + } + return uri; + } + + inline std::string expand(const std::string& curie) const { + assert(curie.find(":") != std::string::npos); + SerdNode curie_node = string_to_node(SERD_CURIE, curie); + SerdChunk uri_prefix; + SerdChunk uri_suffix; + if (serd_env_expand(_c_obj, &curie_node, &uri_prefix, &uri_suffix)) { + std::string ret((const char*)uri_prefix.buf, uri_prefix.len); + ret.append((const char*)uri_suffix.buf, uri_suffix.len); + return ret; + } + std::cerr << "CURIE `" << curie << "' has unknown prefix." << std::endl; + return curie; + } +}; + +/** Sord library state. */ +class World : public boost::noncopyable, public Wrapper { +public: + inline World() + : _next_blank_id(0) + { + _c_obj = sord_world_new(); + add_prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + } + + inline ~World() { + sord_world_free(_c_obj); + } + + inline uint64_t blank_id() { return _next_blank_id++; } + + inline void add_prefix(const std::string& prefix, const std::string& uri) { + _prefixes.add(prefix, uri); + } + + inline const Namespaces& prefixes() const { return _prefixes; } + inline SordWorld world() { return _c_obj; } + +private: + Namespaces _prefixes; + std::set _blank_ids; + uint64_t _next_blank_id; +}; + +/** An RDF Node (resource, literal, etc) + */ +class Node : public Wrapper { +public: + enum Type { + UNKNOWN = 0, + RESOURCE = SORD_URI, + BLANK = SORD_BLANK, + LITERAL = SORD_LITERAL + }; + + inline Node() : Wrapper(NULL), _world(NULL) {} + + inline Node(World& world, Type t, const std::string& s); + inline Node(World& world); + inline Node(World& world, SordNode node); + inline Node(const Node& other); + inline ~Node(); + + inline Type type() const { + return _c_obj ? (Type)sord_node_get_type(_c_obj) : UNKNOWN; + } + + inline SordNode get_node() const { return _c_obj; } + + inline bool is_valid() const { return type() != UNKNOWN; } + + inline bool operator<(const Node& other) const { + if (type() != other.type()) { + return type() < other.type(); + } else { + return to_string() < other.to_string(); + } + } + + const Node& operator=(const Node& other) { + if (_c_obj) + sord_node_free(_c_obj); + _world = other._world; + _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL; + return *this; + } + + inline bool operator==(const Node& other) const { + return sord_node_equals(_c_obj, other._c_obj); + } + + inline const uint8_t* to_u_string() const; + inline const char* to_c_string() const; + inline std::string to_string() const; + + inline bool is_literal_type(const char* type_uri) const; + + inline bool is_resource() const { return _c_obj && type() == RESOURCE; } + inline bool is_blank() const { return _c_obj && type() == BLANK; } + inline bool is_int() const { return is_literal_type(SORD_NS_XSD "integer"); } + inline bool is_float() const { return is_literal_type(SORD_NS_XSD "decimal"); } + inline bool is_bool() const { return is_literal_type(SORD_NS_XSD "boolean"); } + + inline int to_int() const; + inline float to_float() const; + inline bool to_bool() const; + + inline static Node blank_id(World& world, const std::string base="b") { + const uint64_t num = world.blank_id(); + std::ostringstream ss; + ss << base << num; + return Node(world, Node::BLANK, ss.str()); + } + +private: + World* _world; +}; + +inline std::ostream& +operator<<(std::ostream& os, const Node& node) +{ + return os << node.to_string() << std::endl; +} + +class Resource : public Node { +public: + inline Resource(World& world, const std::string& s) : Node(world, Node::RESOURCE, s) {} +}; + +class Curie : public Node { +public: + inline Curie(World& world, const std::string& s) + : Node(world, Node::RESOURCE, world.prefixes().expand(s)) {} +}; + +class Literal : public Node { +public: + inline Literal(World& world, const std::string& s) : Node(world, Node::LITERAL, s) {} +}; + +inline +Node::Node(World& world, Type type, const std::string& s) + : _world(&world) +{ + switch (type) { + case RESOURCE: + assert(s.find(":") == std::string::npos + || s.substr(0, 5) == "http:" + || s.substr(0, 5) == "file:" + || s.substr(0, 4) == "urn:"); + _c_obj = sord_new_uri( + world.world(), (const unsigned char*)s.c_str()); + break; + case LITERAL: + _c_obj = sord_new_literal( + world.world(), NULL, (const unsigned char*)s.c_str(), NULL); + break; + case BLANK: + _c_obj = sord_new_blank( + world.world(), (const unsigned char*)s.c_str()); + break; + default: + _c_obj = NULL; + } + + assert(this->type() == type); +} + +inline +Node::Node(World& world) + : _world(&world) +{ + Node me = blank_id(world); + *this = me; +} + +inline +Node::Node(World& world, SordNode node) + : _world(&world) +{ + _c_obj = node ? sord_node_copy(node) : NULL; +} + +inline +Node::Node(const Node& other) + : Wrapper() + , _world(other._world) +{ + if (_world) { + _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL; + } + + assert((!_c_obj && !other._c_obj) || to_string() == other.to_string()); +} + +inline +Node::~Node() +{ + if (_world) { + sord_node_free(_c_obj); + } +} + + +inline std::string +Node::to_string() const +{ + return std::string(to_c_string()); +} + + +inline const char* +Node::to_c_string() const +{ + return (const char*)sord_node_get_string(_c_obj); +} + +inline const uint8_t* +Node::to_u_string() const +{ + return sord_node_get_string(_c_obj); +} + +inline bool +Node::is_literal_type(const char* type_uri) const +{ + if (_c_obj && sord_node_get_type(_c_obj) == SORD_LITERAL) { + SordNode datatype = sord_node_get_datatype(_c_obj); + if (datatype && !strcmp((const char*)sord_node_get_string(datatype), + type_uri)) + return true; + } + return false; +} + +inline int +Node::to_int() const +{ + assert(is_int()); + std::locale c_locale("C"); + std::stringstream ss((const char*)sord_node_get_string(_c_obj)); + ss.imbue(c_locale); + int i = 0; + ss >> i; + return i; +} + +inline float +Node::to_float() const +{ + assert(is_float()); + std::locale c_locale("C"); + std::stringstream ss((const char*)sord_node_get_string(_c_obj)); + ss.imbue(c_locale); + float f = 0.0f; + ss >> f; + return f; +} + +inline bool +Node::to_bool() const +{ + assert(is_bool()); + return !strcmp((const char*)sord_node_get_string(_c_obj), "true"); +} + +struct Iter : public Wrapper { + inline Iter(World& world, SordIter c_obj) + : Wrapper(c_obj), _world(world) {} + inline ~Iter() { sord_iter_free(_c_obj); } + inline bool end() const { return sord_iter_end(_c_obj); } + inline bool next() const { return sord_iter_next(_c_obj); } + inline Iter& operator++() { assert(!end()); next(); return *this; } + inline Node get_subject() const { + SordQuad quad; + sord_iter_get(_c_obj, quad); + return Node(_world, quad[SORD_SUBJECT]); + } + inline Node get_predicate() const { + SordQuad quad; + sord_iter_get(_c_obj, quad); + return Node(_world, quad[SORD_PREDICATE]); + } + inline Node get_object() const { + SordQuad quad; + sord_iter_get(_c_obj, quad); + return Node(_world, quad[SORD_OBJECT]); + } + World& _world; +}; + + +/** An RDF Model (collection of triples). + */ +class Model : public boost::noncopyable, public Wrapper { +public: + inline Model(World& world, const Glib::ustring& base_uri="."); + inline ~Model(); + + inline const Node& base_uri() const { return _base; } + + inline void load_file(const Glib::ustring& uri); + + inline void load_string(const char* str, + size_t len, + const Glib::ustring& base_uri, + const std::string lang = "turtle"); + + inline void write_to_file_handle(FILE* fd, const char* lang); + inline void write_to_file(const Glib::ustring& uri, const char* lang); + inline char* write_to_string(const char* lang); + + inline void add_statement(const Node& subject, + const Node& predicate, + const Node& object); + + inline Iter find(const Node& subject, + const Node& predicate, + const Node& object); + + inline World& world() const { return _world; } + +private: + World& _world; + Node _base; + SerdWriter _writer; + size_t _next_blank_id; +}; + +/** Create an empty in-memory RDF model. + */ +inline +Model::Model(World& world, const Glib::ustring& base_uri) + : _world(world) + , _base(world, Node::RESOURCE, base_uri) + , _writer(NULL) +{ + // FIXME: parameters + _c_obj = sord_new(_world.world(), SORD_SPO|SORD_OPS, true); +} + +inline void +Model::load_string(const char* str, + size_t len, + const Glib::ustring& base_uri, + const std::string lang) +{ + // TODO +} + +inline Model::~Model() +{ + sord_free(_c_obj); +} + +inline void +Model::load_file(const Glib::ustring& data_uri) +{ + // FIXME: blank prefix + sord_read_file(_c_obj, (const uint8_t*)data_uri.c_str(), NULL, + (const uint8_t*)"b"); +} + +inline void +Model::write_to_file_handle(FILE* fd, const char* lang) +{ + sord_write_file_handle(_c_obj, + _world.prefixes().c_obj(), + fd, + _base.to_u_string(), + NULL, + NULL); +} + +inline void +Model::write_to_file(const Glib::ustring& uri, const char* lang) +{ + sord_write_file(_c_obj, + _world.prefixes().c_obj(), + (const uint8_t*)uri.c_str(), + NULL, + NULL); +} + +inline char* +Model::write_to_string(const char* lang) +{ + std::cerr << "TODO: serialise" << std::endl; + return NULL; +} + +inline void +Model::add_statement(const Node& subject, + const Node& predicate, + const Node& object) +{ + SordQuad quad = { subject.c_obj(), + predicate.c_obj(), + object.c_obj(), + NULL }; + + sord_add(_c_obj, quad); +} + +inline Iter +Model::find(const Node& subject, + const Node& predicate, + const Node& object) +{ + SordQuad quad = { subject.c_obj(), + predicate.c_obj(), + object.c_obj(), + NULL }; + + return Iter(_world, sord_find(_c_obj, quad)); +} + +} // namespace Sord + +#endif // SORD_SORDMM_HPP + -- cgit v1.2.1