aboutsummaryrefslogtreecommitdiffstats
path: root/src/range.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/range.c')
-rw-r--r--src/range.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/range.c b/src/range.c
new file mode 100644
index 00000000..973c6a81
--- /dev/null
+++ b/src/range.c
@@ -0,0 +1,250 @@
+/*
+ Copyright 2011-2018 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "range.h"
+
+#include "iter.h"
+#include "log.h"
+#include "model.h"
+#include "sink.h"
+#include "world.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum { NAMED, ANON_S, ANON_O, LIST_S, LIST_O } NodeStyle;
+
+static SerdStatus
+write_range_statement(SerdSink* sink,
+ const SerdModel* model,
+ unsigned depth,
+ SerdStatementFlags flags,
+ const SerdStatement* statement);
+
+SerdRange*
+serd_range_new(SerdIter* const begin, SerdIter* const end)
+{
+ SerdRange* range = (SerdRange*)malloc(sizeof(SerdRange));
+
+ range->begin = begin;
+ range->end = end;
+
+ return range;
+}
+
+SerdRange*
+serd_range_copy(const SerdRange* range)
+{
+ SerdRange* copy = (SerdRange*)malloc(sizeof(SerdRange));
+
+ memcpy(copy, range, sizeof(SerdRange));
+ copy->begin = serd_iter_copy(range->begin);
+ copy->end = serd_iter_copy(range->end);
+
+ return copy;
+}
+
+void
+serd_range_free(SerdRange* range)
+{
+ serd_iter_free(range->begin);
+ serd_iter_free(range->end);
+ free(range);
+}
+
+const SerdStatement*
+serd_range_front(const SerdRange* range)
+{
+ return serd_iter_get(range->begin);
+}
+
+bool
+serd_range_equals(const SerdRange* lhs, const SerdRange* rhs)
+{
+ return (!lhs && !rhs) ||
+ (lhs && rhs && serd_iter_equals(lhs->begin, rhs->begin) &&
+ serd_iter_equals(lhs->end, rhs->end));
+}
+
+bool
+serd_range_next(SerdRange* range)
+{
+ return serd_iter_next(range->begin);
+}
+
+size_t
+serd_range_size(const SerdRange* range)
+{
+ SerdRange* r = serd_range_copy(range);
+ uint64_t n = 0;
+ for (; !serd_range_empty(r); serd_range_next(r)) {
+ ++n;
+ }
+ serd_range_free(r);
+ return n;
+}
+
+bool
+serd_range_empty(const SerdRange* range)
+{
+ return !range || serd_iter_equals(range->begin, range->end);
+}
+
+const SerdIter*
+serd_range_begin(const SerdRange* range)
+{
+ return range->begin;
+}
+
+const SerdIter*
+serd_range_end(const SerdRange* range)
+{
+ return range->end;
+}
+
+static NodeStyle
+get_node_style(const SerdModel* model, const SerdNode* object)
+{
+ const size_t num_as_object =
+ serd_model_count(model, NULL, NULL, object, NULL);
+ if (serd_node_get_type(object) != SERD_BLANK || num_as_object > 1) {
+ return NAMED; // Non-blank and/or blank referred to several times
+ }
+
+ if (serd_model_ask(model, object, model->world->rdf_first, NULL, NULL) &&
+ serd_model_ask(model, object, model->world->rdf_rest, NULL, NULL)) {
+ return num_as_object == 0 ? LIST_S : LIST_O;
+ }
+
+ return num_as_object == 0 ? ANON_S : ANON_O;
+}
+
+static SerdStatus
+write_range_internal(SerdSink* sink,
+ const unsigned depth,
+ const SerdStatementFlags flags,
+ SerdRange* range)
+{
+ (void)flags;
+
+ SerdStatus st = SERD_SUCCESS;
+ const SerdModel* model = range && range->begin ? range->begin->model : NULL;
+ for (; !st && !serd_range_empty(range); serd_range_next(range)) {
+ st = write_range_statement(
+ sink, model, depth, 0, serd_range_front(range));
+ }
+
+ return st;
+}
+
+static SerdStatus
+write_list(SerdSink* sink,
+ const SerdModel* model,
+ const unsigned depth,
+ const SerdNode* object)
+{
+ SerdStatus st = SERD_SUCCESS;
+ const SerdWorld* world = model->world;
+ const SerdNode* first = world->rdf_first;
+ const SerdNode* rest = world->rdf_rest;
+ const SerdNode* nil = world->rdf_nil;
+
+ SerdIter* f = serd_model_find(model, object, first, NULL, NULL);
+ while (!st && f && !serd_node_equals(object, nil)) {
+ const SerdStatement* fs = serd_iter_get(f);
+ st = write_range_statement(sink, model, depth + 1, 0, fs);
+ serd_iter_free(f);
+
+ SerdIter* const r = serd_model_find(model, object, rest, NULL, NULL);
+ const SerdNode* next =
+ r ? serd_statement_get_object(serd_iter_get(r)) : NULL;
+
+ f = next ? serd_model_find(model, next, first, NULL, NULL) : NULL;
+ if (r && f) {
+ // This and next node are good, write rdf:next statement
+ st = serd_sink_write_statement(sink, 0, serd_iter_get(r));
+ object = next;
+ } else {
+ // Terminate malformed list
+ st = serd_sink_write(
+ sink, 0, object, rest, nil, serd_statement_get_graph(fs));
+ f = NULL;
+ }
+
+ serd_iter_free(r);
+ }
+
+ return st;
+}
+
+static SerdStatus
+write_range_statement(SerdSink* sink,
+ const SerdModel* model,
+ const unsigned depth,
+ SerdStatementFlags flags,
+ const SerdStatement* statement)
+{
+ const SerdNode* subject = serd_statement_get_subject(statement);
+ const NodeStyle subject_style = get_node_style(model, subject);
+
+ if (depth == 0 && (subject_style == ANON_O || subject_style == LIST_O)) {
+ return SERD_SUCCESS; // Skip subject that will be inlined somewhere
+ } else if (subject_style == ANON_S) {
+ flags |= SERD_EMPTY_S; // Write anonymous subjects in "[] p o" style
+ }
+
+ const SerdNode* object = serd_statement_get_object(statement);
+ const NodeStyle object_style = get_node_style(model, object);
+ SerdStatus st = SERD_SUCCESS;
+ if (object_style == ANON_O) {
+ flags |= SERD_ANON_O;
+
+ SerdRange* subrange = serd_model_range(model, object, NULL, NULL, NULL);
+ st = st ? st : serd_sink_write_statement(sink, flags, statement);
+ st = st ? st : write_range_internal(sink, depth + 1, 0, subrange);
+ st = st ? st : serd_sink_end(sink, object);
+ serd_range_free(subrange);
+
+ return st;
+ } else if (object_style == LIST_O) {
+ flags |= SERD_LIST_O;
+ st = st ? st : serd_sink_write_statement(sink, flags, statement);
+ st = st ? st : write_list(sink, model, depth + 1, object);
+ return st;
+ }
+
+ return serd_sink_write_statement(sink, flags, statement);
+}
+
+SerdStatus
+serd_range_serialise(const SerdRange* range,
+ SerdSink* sink,
+ const SerdSerialisationFlags flags)
+{
+ SerdStatus st = SERD_SUCCESS;
+ SerdRange* const copy = serd_range_copy(range);
+
+ if (flags & SERD_NO_INLINE_OBJECTS) {
+ for (; !st && !serd_range_empty(copy); serd_range_next(copy)) {
+ st = sink->statement(sink->handle, 0, serd_range_front(copy));
+ }
+ } else {
+ st = write_range_internal(sink, 0, 0, copy);
+ }
+
+ serd_range_free(copy);
+ return st;
+}