comparison osx/include/sord/sordmm.hpp @ 31:642a72ce5e62

Add Vamp SDK, move serd/sord to more obvious locations
author Chris Cannam
date Fri, 04 Jul 2014 08:16:02 +0100
parents osx/include/sord-0/sord/sordmm.hpp@cc5d363db385
children
comparison
equal deleted inserted replaced
24:3c5adc4a864f 31:642a72ce5e62
1 /*
2 Copyright 2011-2013 David Robillard <http://drobilla.net>
3
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.
7
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.
15 */
16
17 /**
18 @file sordmm.hpp
19 Public Sord C++ API.
20 */
21
22 #ifndef SORD_SORDMM_HPP
23 #define SORD_SORDMM_HPP
24
25 #include <cassert>
26 #include <cstring>
27 #include <cstdlib>
28 #include <iostream>
29 #include <set>
30 #include <string>
31 #include <sstream>
32
33 #include "serd/serd.h"
34 #include "sord/sord.h"
35
36 #define SORD_NS_XSD "http://www.w3.org/2001/XMLSchema#"
37
38 namespace Sord {
39
40 /** Utility base class to prevent copying. */
41 class Noncopyable {
42 protected:
43 Noncopyable() {}
44 ~Noncopyable() {}
45 private:
46 Noncopyable(const Noncopyable&);
47 const Noncopyable& operator=(const Noncopyable&);
48 };
49
50 /** C++ wrapper for a Sord object. */
51 template <typename T>
52 class Wrapper {
53 public:
54 inline Wrapper(T c_obj = NULL) : _c_obj(c_obj) {}
55
56 inline T c_obj() { return _c_obj; }
57 inline const T c_obj() const { return _c_obj; }
58
59 protected:
60 T _c_obj;
61 };
62
63 /** Collection of RDF namespaces with prefixes. */
64 class Namespaces : public Wrapper<SerdEnv*> {
65 public:
66 Namespaces() : Wrapper<SerdEnv*>(serd_env_new(NULL)) {}
67 ~Namespaces() { serd_env_free(_c_obj); }
68
69 static inline SerdNode string_to_node(SerdType type, const std::string& s) {
70 SerdNode ret = {
71 (const uint8_t*)s.c_str(), s.length(), s.length(), 0, type };
72 return ret;
73 }
74
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);
80 }
81
82 inline std::string qualify(std::string uri) const {
83 const SerdNode uri_node = string_to_node(SERD_URI, uri);
84 SerdNode prefix;
85 SerdChunk suffix;
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);
89 return ret;
90 }
91 return uri;
92 }
93
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);
97 SerdChunk uri_prefix;
98 SerdChunk uri_suffix;
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);
102 return ret;
103 }
104 std::cerr << "CURIE `" << curie << "' has unknown prefix." << std::endl;
105 return curie;
106 }
107 };
108
109 /** Sord library state. */
110 class World : public Noncopyable, public Wrapper<SordWorld*> {
111 public:
112 inline World()
113 : _next_blank_id(0)
114 {
115 _c_obj = sord_world_new();
116 }
117
118 inline ~World() {
119 sord_world_free(_c_obj);
120 }
121
122 inline uint64_t blank_id() { return _next_blank_id++; }
123
124 inline void add_prefix(const std::string& prefix, const std::string& uri) {
125 _prefixes.add(prefix, uri);
126 }
127
128 inline const Namespaces& prefixes() const { return _prefixes; }
129 inline SordWorld* world() { return _c_obj; }
130
131 private:
132 Namespaces _prefixes;
133 std::set<std::string> _blank_ids;
134 uint64_t _next_blank_id;
135 };
136
137 /** An RDF Node (resource, literal, etc)
138 */
139 class Node : public Wrapper<SordNode*> {
140 public:
141 enum Type {
142 UNKNOWN = 0,
143 URI = SORD_URI,
144 BLANK = SORD_BLANK,
145 LITERAL = SORD_LITERAL
146 };
147
148 inline Node() : Wrapper<SordNode*>(NULL), _world(NULL) {}
149
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);
155 inline ~Node();
156
157 inline Type type() const {
158 return _c_obj ? (Type)sord_node_get_type(_c_obj) : UNKNOWN;
159 }
160
161 inline const SordNode* get_node() const { return _c_obj; }
162 inline SordNode* get_node() { return _c_obj; }
163
164 const SerdNode* to_serd_node() {
165 return sord_node_to_serd_node(_c_obj);
166 }
167
168 inline bool is_valid() const { return type() != UNKNOWN; }
169
170 inline bool operator<(const Node& other) const {
171 if (type() != other.type()) {
172 return type() < other.type();
173 } else {
174 return to_string() < other.to_string();
175 }
176 }
177
178 Node& operator=(const Node& other) {
179 if (&other != this) {
180 if (_c_obj) {
181 sord_node_free(_world->c_obj(), _c_obj);
182 }
183 _world = other._world;
184 _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL;
185 }
186 return *this;
187 }
188
189 inline bool operator==(const Node& other) const {
190 return sord_node_equals(_c_obj, other._c_obj);
191 }
192
193 inline const uint8_t* to_u_string() const;
194 inline const char* to_c_string() const;
195 inline std::string to_string() const;
196
197 inline bool is_literal_type(const char* type_uri) const;
198
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"); }
204
205 inline int to_int() const;
206 inline float to_float() const;
207 inline bool to_bool() const;
208
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;
212 ss << base << num;
213 return Node(world, Node::BLANK, ss.str());
214 }
215
216 private:
217 World* _world;
218 };
219
220 inline std::ostream&
221 operator<<(std::ostream& os, const Node& node)
222 {
223 return os << node.to_string();
224 }
225
226 class URI : public Node {
227 public:
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()))
234 {}
235 };
236
237 class Curie : public Node {
238 public:
239 inline Curie(World& world, const std::string& s)
240 : Node(world, Node::URI, world.prefixes().expand(s)) {}
241 };
242
243 class Literal : public Node {
244 public:
245 inline Literal(World& world, const std::string& s)
246 : Node(world, Node::LITERAL, s) {}
247
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");
252
253 return Node(
254 world,
255 sord_node_from_serd_node(
256 world.c_obj(), world.prefixes().c_obj(), &val, &type, NULL),
257 false);
258 }
259
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");
264
265 return Node(
266 world,
267 sord_node_from_serd_node(
268 world.c_obj(), world.prefixes().c_obj(), &val, &type, NULL),
269 false);
270 }
271 };
272
273 inline
274 Node::Node(World& world, Type type, const std::string& s)
275 : _world(&world)
276 {
277 switch (type) {
278 case URI:
279 _c_obj = sord_new_uri(
280 world.world(), (const unsigned char*)s.c_str());
281 break;
282 case LITERAL:
283 _c_obj = sord_new_literal(
284 world.world(), NULL, (const unsigned char*)s.c_str(), NULL);
285 break;
286 case BLANK:
287 _c_obj = sord_new_blank(
288 world.world(), (const unsigned char*)s.c_str());
289 break;
290 default:
291 _c_obj = NULL;
292 }
293
294 assert(this->type() == type);
295 }
296
297 inline
298 Node::Node(World& world)
299 : _world(&world)
300 {
301 Node me = blank_id(world);
302 *this = me;
303 }
304
305 inline
306 Node::Node(World& world, const SordNode* node)
307 : _world(&world)
308 {
309 _c_obj = sord_node_copy(node);
310 }
311
312 inline
313 Node::Node(World& world, SordNode* node, bool copy)
314 : _world(&world)
315 {
316 _c_obj = copy ? sord_node_copy(node) : node;
317 }
318
319 inline
320 Node::Node(const Node& other)
321 : Wrapper<SordNode*>()
322 , _world(other._world)
323 {
324 if (_world) {
325 _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL;
326 }
327
328 assert((!_c_obj && !other._c_obj) || to_string() == other.to_string());
329 }
330
331 inline
332 Node::~Node()
333 {
334 if (_world) {
335 sord_node_free(_world->c_obj(), _c_obj);
336 }
337 }
338
339 inline std::string
340 Node::to_string() const
341 {
342 return _c_obj ? (const char*)sord_node_get_string(_c_obj) : "";
343 }
344
345 inline const char*
346 Node::to_c_string() const
347 {
348 return (const char*)sord_node_get_string(_c_obj);
349 }
350
351 inline const uint8_t*
352 Node::to_u_string() const
353 {
354 return sord_node_get_string(_c_obj);
355 }
356
357 inline bool
358 Node::is_literal_type(const char* type_uri) const
359 {
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),
363 type_uri))
364 return true;
365 }
366 return false;
367 }
368
369 inline int
370 Node::to_int() const
371 {
372 assert(is_int());
373 char* endptr;
374 return strtol((const char*)sord_node_get_string(_c_obj), &endptr, 10);
375 }
376
377 inline float
378 Node::to_float() const
379 {
380 assert(is_float());
381 char* endptr;
382 return serd_strtod((const char*)sord_node_get_string(_c_obj), &endptr);
383 }
384
385 inline bool
386 Node::to_bool() const
387 {
388 assert(is_bool());
389 return !strcmp((const char*)sord_node_get_string(_c_obj), "true");
390 }
391
392 struct Iter : public Wrapper<SordIter*> {
393 inline Iter(World& world, SordIter* c_obj)
394 : Wrapper<SordIter*>(c_obj), _world(world) {}
395 inline ~Iter() { sord_iter_free(_c_obj); }
396 inline bool end() const { return sord_iter_end(_c_obj); }
397 inline bool next() const { return sord_iter_next(_c_obj); }
398 inline Iter& operator++() {
399 assert(!end());
400 next();
401 return *this;
402 }
403 inline const Node get_subject() const {
404 SordQuad quad;
405 sord_iter_get(_c_obj, quad);
406 return Node(_world, quad[SORD_SUBJECT]);
407 }
408 inline const Node get_predicate() const {
409 SordQuad quad;
410 sord_iter_get(_c_obj, quad);
411 return Node(_world, quad[SORD_PREDICATE]);
412 }
413 inline const Node get_object() const {
414 SordQuad quad;
415 sord_iter_get(_c_obj, quad);
416 return Node(_world, quad[SORD_OBJECT]);
417 }
418 World& _world;
419 };
420
421 /** An RDF Model (collection of triples).
422 */
423 class Model : public Noncopyable, public Wrapper<SordModel*> {
424 public:
425 inline Model(World& world,
426 const std::string& base_uri,
427 unsigned indices = (SORD_SPO | SORD_OPS),
428 bool graphs = true);
429
430 inline ~Model();
431
432 inline const Node& base_uri() const { return _base; }
433
434 size_t num_quads() const { return sord_num_quads(_c_obj); }
435
436 inline void load_file(SerdEnv* env,
437 SerdSyntax syntax,
438 const std::string& uri,
439 const std::string& base_uri="");
440
441 inline void load_string(SerdEnv* env,
442 SerdSyntax syntax,
443 const char* str,
444 size_t len,
445 const std::string& base_uri);
446
447 inline SerdStatus write_to_file(
448 const std::string& uri,
449 SerdSyntax syntax = SERD_TURTLE,
450 SerdStyle style = (SerdStyle)(SERD_STYLE_ABBREVIATED
451 |SERD_STYLE_CURIED
452 |SERD_STYLE_RESOLVED));
453
454 inline std::string write_to_string(
455 const std::string& base_uri,
456 SerdSyntax syntax = SERD_TURTLE,
457 SerdStyle style = (SerdStyle)(SERD_STYLE_ABBREVIATED
458 |SERD_STYLE_CURIED
459 |SERD_STYLE_RESOLVED));
460
461 inline void add_statement(const Node& subject,
462 const Node& predicate,
463 const Node& object);
464
465 inline Iter find(const Node& subject,
466 const Node& predicate,
467 const Node& object);
468
469 inline Node get(const Node& subject,
470 const Node& predicate,
471 const Node& object);
472
473 inline World& world() const { return _world; }
474
475 private:
476 World& _world;
477 Node _base;
478 SerdWriter* _writer;
479 size_t _next_blank_id;
480 };
481
482 /** Create an empty in-memory RDF model.
483 */
484 inline
485 Model::Model(World& world,
486 const std::string& base_uri,
487 unsigned indices,
488 bool graphs)
489 : _world(world)
490 , _base(world, Node::URI, base_uri)
491 , _writer(NULL)
492 {
493 _c_obj = sord_new(_world.world(), indices, graphs);
494 }
495
496 inline void
497 Model::load_string(SerdEnv* env,
498 SerdSyntax syntax,
499 const char* str,
500 size_t len,
501 const std::string& base_uri)
502 {
503 SerdReader* reader = sord_new_reader(_c_obj, env, syntax, NULL);
504 serd_reader_read_string(reader, (const uint8_t*)str);
505 serd_reader_free(reader);
506 }
507
508 inline Model::~Model()
509 {
510 sord_free(_c_obj);
511 }
512
513 inline void
514 Model::load_file(SerdEnv* env,
515 SerdSyntax syntax,
516 const std::string& data_uri,
517 const std::string& base_uri)
518 {
519 uint8_t* path = serd_file_uri_parse((const uint8_t*)data_uri.c_str(), NULL);
520 if (!path) {
521 fprintf(stderr, "Failed to parse file URI <%s>\n", data_uri.c_str());
522 return;
523 }
524
525 // FIXME: blank prefix parameter?
526 SerdReader* reader = sord_new_reader(_c_obj, env, syntax, NULL);
527 serd_reader_read_file(reader, path);
528 serd_reader_free(reader);
529 free(path);
530 }
531
532 inline SerdStatus
533 Model::write_to_file(const std::string& uri, SerdSyntax syntax, SerdStyle style)
534 {
535 uint8_t* path = serd_file_uri_parse((const uint8_t*)uri.c_str(), NULL);
536 if (!path) {
537 fprintf(stderr, "Failed to parse file URI <%s>\n", uri.c_str());
538 return SERD_ERR_BAD_ARG;
539 }
540
541 FILE* const fd = fopen((const char*)path, "w");
542 if (!fd) {
543 fprintf(stderr, "Failed to open file %s\n", path);
544 free(path);
545 return SERD_ERR_UNKNOWN;
546 }
547 free(path);
548
549 SerdURI base_uri = SERD_URI_NULL;
550 if (serd_uri_parse((const uint8_t*)uri.c_str(), &base_uri)) {
551 fprintf(stderr, "Invalid base URI <%s>\n", uri.c_str());
552 fclose(fd);
553 return SERD_ERR_BAD_ARG;
554 }
555
556 SerdWriter* writer = serd_writer_new(syntax,
557 style,
558 _world.prefixes().c_obj(),
559 &base_uri,
560 serd_file_sink,
561 fd);
562
563 serd_env_foreach(_world.prefixes().c_obj(),
564 (SerdPrefixSink)serd_writer_set_prefix,
565 writer);
566
567 sord_write(_c_obj, writer, 0);
568 serd_writer_free(writer);
569 fclose(fd);
570
571 return SERD_SUCCESS;
572 }
573
574 static size_t
575 string_sink(const void* buf, size_t len, void* stream)
576 {
577 std::string* str = (std::string*)stream;
578 str->append((const char*)buf, len);
579 return len;
580 }
581
582 inline std::string
583 Model::write_to_string(const std::string& base_uri_str,
584 SerdSyntax syntax,
585 SerdStyle style)
586 {
587 SerdURI base_uri = SERD_URI_NULL;
588 if (serd_uri_parse((const uint8_t*)base_uri_str.c_str(), &base_uri)) {
589 fprintf(stderr, "Invalid base URI <%s>\n", base_uri_str.c_str());
590 return "";
591 }
592
593 std::string ret;
594
595 SerdWriter* writer = serd_writer_new(syntax,
596 style,
597 _world.prefixes().c_obj(),
598 &base_uri,
599 string_sink,
600 &ret);
601
602 serd_env_foreach(_world.prefixes().c_obj(),
603 (SerdPrefixSink)serd_writer_set_prefix,
604 writer);
605
606 sord_write(_c_obj, writer, 0);
607
608 serd_writer_free(writer);
609 return ret;
610 }
611
612 inline void
613 Model::add_statement(const Node& subject,
614 const Node& predicate,
615 const Node& object)
616 {
617 SordQuad quad = { subject.c_obj(),
618 predicate.c_obj(),
619 object.c_obj(),
620 NULL };
621
622 sord_add(_c_obj, quad);
623 }
624
625 inline Iter
626 Model::find(const Node& subject,
627 const Node& predicate,
628 const Node& object)
629 {
630 SordQuad quad = { subject.c_obj(),
631 predicate.c_obj(),
632 object.c_obj(),
633 NULL };
634
635 return Iter(_world, sord_find(_c_obj, quad));
636 }
637
638 inline Node
639 Model::get(const Node& subject,
640 const Node& predicate,
641 const Node& object)
642 {
643 SordNode* c_node = sord_get(
644 _c_obj, subject.c_obj(), predicate.c_obj(), object.c_obj(), NULL);
645 Node node(_world, c_node);
646 sord_node_free(_world.c_obj(), c_node);
647 return node;
648 }
649
650 } // namespace Sord
651
652 #endif // SORD_SORDMM_HPP
653