2 Copyright 2011-2013 David Robillard <http://drobilla.net>
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 #ifndef SORD_SORDMM_HPP
23 #define SORD_SORDMM_HPP
33 #include "serd/serd.h"
34 #include "sord/sord.h"
36 #define SORD_NS_XSD "http://www.w3.org/2001/XMLSchema#"
40 /** Utility base class to prevent copying. */
46 Noncopyable(const Noncopyable
&);
47 const Noncopyable
& operator=(const Noncopyable
&);
50 /** C++ wrapper for a Sord object. */
54 inline Wrapper(T c_obj
= NULL
) : _c_obj(c_obj
) {}
56 inline T
c_obj() { return _c_obj
; }
57 inline const T
c_obj() const { return _c_obj
; }
63 /** Collection of RDF namespaces with prefixes. */
64 class Namespaces
: public Wrapper
<SerdEnv
*> {
66 Namespaces() : Wrapper
<SerdEnv
*>(serd_env_new(NULL
)) {}
67 ~Namespaces() { serd_env_free(_c_obj
); }
69 static inline SerdNode
string_to_node(SerdType type
, const std::string
& s
) {
71 (const uint8_t*)s
.c_str(), s
.length(), s
.length(), 0, type
};
75 inline void add(const std::string
& name
,
76 const std::string
& uri
) {
77 const SerdNode name_node
= string_to_node(SERD_LITERAL
, name
);
78 const SerdNode uri_node
= string_to_node(SERD_URI
, uri
);
79 serd_env_set_prefix(_c_obj
, &name_node
, &uri_node
);
82 inline std::string
qualify(std::string uri
) const {
83 const SerdNode uri_node
= string_to_node(SERD_URI
, uri
);
86 if (serd_env_qualify(_c_obj
, &uri_node
, &prefix
, &suffix
)) {
87 std::string
ret((const char*)prefix
.buf
, prefix
.n_bytes
);
88 ret
.append(":").append((const char*)suffix
.buf
, suffix
.len
);
94 inline std::string
expand(const std::string
& curie
) const {
95 assert(curie
.find(":") != std::string::npos
);
96 SerdNode curie_node
= string_to_node(SERD_CURIE
, curie
);
99 if (!serd_env_expand(_c_obj
, &curie_node
, &uri_prefix
, &uri_suffix
)) {
100 std::string
ret((const char*)uri_prefix
.buf
, uri_prefix
.len
);
101 ret
.append((const char*)uri_suffix
.buf
, uri_suffix
.len
);
104 std::cerr
<< "CURIE `" << curie
<< "' has unknown prefix." << std::endl
;
109 /** Sord library state. */
110 class World
: public Noncopyable
, public Wrapper
<SordWorld
*> {
115 _c_obj
= sord_world_new();
119 sord_world_free(_c_obj
);
122 inline uint64_t blank_id() { return _next_blank_id
++; }
124 inline void add_prefix(const std::string
& prefix
, const std::string
& uri
) {
125 _prefixes
.add(prefix
, uri
);
128 inline const Namespaces
& prefixes() const { return _prefixes
; }
129 inline SordWorld
* world() { return _c_obj
; }
132 Namespaces _prefixes
;
133 std::set
<std::string
> _blank_ids
;
134 uint64_t _next_blank_id
;
137 /** An RDF Node (resource, literal, etc)
139 class Node
: public Wrapper
<SordNode
*> {
145 LITERAL
= SORD_LITERAL
148 inline Node() : Wrapper
<SordNode
*>(NULL
), _world(NULL
) {}
150 inline Node(World
& world
, Type t
, const std::string
& s
);
151 inline Node(World
& world
);
152 inline Node(World
& world
, const SordNode
* node
);
153 inline Node(World
& world
, SordNode
* node
, bool copy
=false);
154 inline Node(const Node
& other
);
157 inline Type
type() const {
158 return _c_obj
? (Type
)sord_node_get_type(_c_obj
) : UNKNOWN
;
161 inline const SordNode
* get_node() const { return _c_obj
; }
162 inline SordNode
* get_node() { return _c_obj
; }
164 const SerdNode
* to_serd_node() {
165 return sord_node_to_serd_node(_c_obj
);
168 inline bool is_valid() const { return type() != UNKNOWN
; }
170 inline bool operator<(const Node
& other
) const {
171 if (type() != other
.type()) {
172 return type() < other
.type();
174 return to_string() < other
.to_string();
178 Node
& operator=(const Node
& other
) {
179 if (&other
!= this) {
181 sord_node_free(_world
->c_obj(), _c_obj
);
183 _world
= other
._world
;
184 _c_obj
= other
._c_obj
? sord_node_copy(other
._c_obj
) : NULL
;
189 inline bool operator==(const Node
& other
) const {
190 return sord_node_equals(_c_obj
, other
._c_obj
);
193 inline const uint8_t* to_u_string() const;
194 inline const char* to_c_string() const;
195 inline std::string
to_string() const;
197 inline bool is_literal_type(const char* type_uri
) const;
199 inline bool is_uri() const { return _c_obj
&& type() == URI
; }
200 inline bool is_blank() const { return _c_obj
&& type() == BLANK
; }
201 inline bool is_int() const { return is_literal_type(SORD_NS_XSD
"integer"); }
202 inline bool is_float() const { return is_literal_type(SORD_NS_XSD
"decimal"); }
203 inline bool is_bool() const { return is_literal_type(SORD_NS_XSD
"boolean"); }
205 inline int to_int() const;
206 inline float to_float() const;
207 inline bool to_bool() const;
209 inline static Node
blank_id(World
& world
, const std::string base
="b") {
210 const uint64_t num
= world
.blank_id();
211 std::ostringstream ss
;
213 return Node(world
, Node::BLANK
, ss
.str());
221 operator<<(std::ostream
& os
, const Node
& node
)
223 return os
<< node
.to_string();
226 class URI
: public Node
{
228 inline URI(World
& world
, const std::string
& s
)
229 : Node(world
, Node::URI
, s
) {}
230 inline URI(World
& world
, const std::string
& s
, const std::string
& base
)
231 : Node(world
, sord_new_relative_uri(world
.world(),
232 (const uint8_t*)s
.c_str(),
233 (const uint8_t*)base
.c_str()))
237 class Curie
: public Node
{
239 inline Curie(World
& world
, const std::string
& s
)
240 : Node(world
, Node::URI
, world
.prefixes().expand(s
)) {}
243 class Literal
: public Node
{
245 inline Literal(World
& world
, const std::string
& s
)
246 : Node(world
, Node::LITERAL
, s
) {}
248 static inline Node
decimal(World
& world
, double d
, unsigned frac_digits
) {
249 const SerdNode val
= serd_node_new_decimal(d
, 7);
250 const SerdNode type
= serd_node_from_string(
251 SERD_URI
, (const uint8_t*)SORD_NS_XSD
"decimal");
255 sord_node_from_serd_node(
256 world
.c_obj(), world
.prefixes().c_obj(), &val
, &type
, NULL
),
260 static inline Node
integer(World
& world
, int64_t i
) {
261 const SerdNode val
= serd_node_new_integer(i
);
262 const SerdNode type
= serd_node_from_string(
263 SERD_URI
, (const uint8_t*)SORD_NS_XSD
"integer");
267 sord_node_from_serd_node(
268 world
.c_obj(), world
.prefixes().c_obj(), &val
, &type
, NULL
),
274 Node::Node(World
& world
, Type type
, const std::string
& s
)
279 _c_obj
= sord_new_uri(
280 world
.world(), (const unsigned char*)s
.c_str());
283 _c_obj
= sord_new_literal(
284 world
.world(), NULL
, (const unsigned char*)s
.c_str(), NULL
);
287 _c_obj
= sord_new_blank(
288 world
.world(), (const unsigned char*)s
.c_str());
294 assert(this->type() == type
);
298 Node::Node(World
& world
)
301 Node me
= blank_id(world
);
306 Node::Node(World
& world
, const SordNode
* node
)
309 _c_obj
= sord_node_copy(node
);
313 Node::Node(World
& world
, SordNode
* node
, bool copy
)
316 _c_obj
= copy
? sord_node_copy(node
) : node
;
320 Node::Node(const Node
& other
)
321 : Wrapper
<SordNode
*>()
322 , _world(other
._world
)
325 _c_obj
= other
._c_obj
? sord_node_copy(other
._c_obj
) : NULL
;
328 assert((!_c_obj
&& !other
._c_obj
) || to_string() == other
.to_string());
335 sord_node_free(_world
->c_obj(), _c_obj
);
340 Node::to_string() const
342 return _c_obj
? (const char*)sord_node_get_string(_c_obj
) : "";
346 Node::to_c_string() const
348 return (const char*)sord_node_get_string(_c_obj
);
351 inline const uint8_t*
352 Node::to_u_string() const
354 return sord_node_get_string(_c_obj
);
358 Node::is_literal_type(const char* type_uri
) const
360 if (_c_obj
&& sord_node_get_type(_c_obj
) == SORD_LITERAL
) {
361 const SordNode
* datatype
= sord_node_get_datatype(_c_obj
);
362 if (datatype
&& !strcmp((const char*)sord_node_get_string(datatype
),
374 return strtol((const char*)sord_node_get_string(_c_obj
), &endptr
, 10);
378 Node::to_float() const
381 return serd_strtod((const char*)sord_node_get_string(_c_obj
), NULL
);
385 Node::to_bool() const
388 return !strcmp((const char*)sord_node_get_string(_c_obj
), "true");
391 struct Iter
: public Wrapper
<SordIter
*> {
392 inline Iter(World
& world
, SordIter
* c_obj
)
393 : Wrapper
<SordIter
*>(c_obj
), _world(world
) {}
394 inline ~Iter() { sord_iter_free(_c_obj
); }
395 inline bool end() const { return sord_iter_end(_c_obj
); }
396 inline bool next() const { return sord_iter_next(_c_obj
); }
397 inline Iter
& operator++() {
402 inline const Node
get_subject() const {
404 sord_iter_get(_c_obj
, quad
);
405 return Node(_world
, quad
[SORD_SUBJECT
]);
407 inline const Node
get_predicate() const {
409 sord_iter_get(_c_obj
, quad
);
410 return Node(_world
, quad
[SORD_PREDICATE
]);
412 inline const Node
get_object() const {
414 sord_iter_get(_c_obj
, quad
);
415 return Node(_world
, quad
[SORD_OBJECT
]);
420 /** An RDF Model (collection of triples).
422 class Model
: public Noncopyable
, public Wrapper
<SordModel
*> {
424 inline Model(World
& world
,
425 const std::string
& base_uri
,
426 unsigned indices
= (SORD_SPO
| SORD_OPS
),
431 inline const Node
& base_uri() const { return _base
; }
433 size_t num_quads() const { return sord_num_quads(_c_obj
); }
435 inline void load_file(SerdEnv
* env
,
437 const std::string
& uri
,
438 const std::string
& base_uri
="");
440 inline void load_string(SerdEnv
* env
,
444 const std::string
& base_uri
);
446 inline SerdStatus
write_to_file(
447 const std::string
& uri
,
448 SerdSyntax syntax
= SERD_TURTLE
,
449 SerdStyle style
= (SerdStyle
)(SERD_STYLE_ABBREVIATED
451 |SERD_STYLE_RESOLVED
));
453 inline std::string
write_to_string(
454 const std::string
& base_uri
,
455 SerdSyntax syntax
= SERD_TURTLE
,
456 SerdStyle style
= (SerdStyle
)(SERD_STYLE_ABBREVIATED
458 |SERD_STYLE_RESOLVED
));
460 inline void add_statement(const Node
& subject
,
461 const Node
& predicate
,
464 inline Iter
find(const Node
& subject
,
465 const Node
& predicate
,
468 inline Node
get(const Node
& subject
,
469 const Node
& predicate
,
472 inline World
& world() const { return _world
; }
479 /** Create an empty in-memory RDF model.
482 Model::Model(World
& world
,
483 const std::string
& base_uri
,
487 , _base(world
, Node::URI
, base_uri
)
489 _c_obj
= sord_new(_world
.world(), indices
, graphs
);
493 Model::load_string(SerdEnv
* env
,
497 const std::string
& base_uri
)
499 SerdReader
* reader
= sord_new_reader(_c_obj
, env
, syntax
, NULL
);
500 serd_reader_read_string(reader
, (const uint8_t*)str
);
501 serd_reader_free(reader
);
504 inline Model::~Model()
510 Model::load_file(SerdEnv
* env
,
512 const std::string
& data_uri
,
513 const std::string
& base_uri
)
515 uint8_t* path
= serd_file_uri_parse((const uint8_t*)data_uri
.c_str(), NULL
);
517 fprintf(stderr
, "Failed to parse file URI <%s>\n", data_uri
.c_str());
521 // FIXME: blank prefix parameter?
522 SerdReader
* reader
= sord_new_reader(_c_obj
, env
, syntax
, NULL
);
523 serd_reader_read_file(reader
, path
);
524 serd_reader_free(reader
);
529 Model::write_to_file(const std::string
& uri
, SerdSyntax syntax
, SerdStyle style
)
531 uint8_t* path
= serd_file_uri_parse((const uint8_t*)uri
.c_str(), NULL
);
533 fprintf(stderr
, "Failed to parse file URI <%s>\n", uri
.c_str());
534 return SERD_ERR_BAD_ARG
;
537 FILE* const fd
= fopen((const char*)path
, "w");
539 fprintf(stderr
, "Failed to open file %s\n", path
);
541 return SERD_ERR_UNKNOWN
;
545 SerdURI base_uri
= SERD_URI_NULL
;
546 if (serd_uri_parse((const uint8_t*)uri
.c_str(), &base_uri
)) {
547 fprintf(stderr
, "Invalid base URI <%s>\n", uri
.c_str());
549 return SERD_ERR_BAD_ARG
;
552 SerdWriter
* writer
= serd_writer_new(syntax
,
554 _world
.prefixes().c_obj(),
559 serd_env_foreach(_world
.prefixes().c_obj(),
560 (SerdPrefixSink
)serd_writer_set_prefix
,
563 sord_write(_c_obj
, writer
, 0);
564 serd_writer_free(writer
);
571 string_sink(const void* buf
, size_t len
, void* stream
)
573 std::string
* str
= (std::string
*)stream
;
574 str
->append((const char*)buf
, len
);
579 Model::write_to_string(const std::string
& base_uri_str
,
583 SerdURI base_uri
= SERD_URI_NULL
;
584 if (serd_uri_parse((const uint8_t*)base_uri_str
.c_str(), &base_uri
)) {
585 fprintf(stderr
, "Invalid base URI <%s>\n", base_uri_str
.c_str());
591 SerdWriter
* writer
= serd_writer_new(syntax
,
593 _world
.prefixes().c_obj(),
598 const SerdNode base_uri_node
= serd_node_from_string(
599 SERD_URI
, (const uint8_t*)base_uri_str
.c_str());
600 serd_writer_set_base_uri(writer
, &base_uri_node
);
602 serd_env_foreach(_world
.prefixes().c_obj(),
603 (SerdPrefixSink
)serd_writer_set_prefix
,
606 sord_write(_c_obj
, writer
, 0);
608 serd_writer_free(writer
);
613 Model::add_statement(const Node
& subject
,
614 const Node
& predicate
,
617 SordQuad quad
= { subject
.c_obj(),
622 sord_add(_c_obj
, quad
);
626 Model::find(const Node
& subject
,
627 const Node
& predicate
,
630 SordQuad quad
= { subject
.c_obj(),
635 return Iter(_world
, sord_find(_c_obj
, quad
));
639 Model::get(const Node
& subject
,
640 const Node
& predicate
,
643 SordNode
* c_node
= sord_get(
644 _c_obj
, subject
.c_obj(), predicate
.c_obj(), object
.c_obj(), NULL
);
645 return Node(_world
, c_node
, false);
650 #endif // SORD_SORDMM_HPP