diff options
author | David Robillard <d@drobilla.net> | 2018-05-12 13:28:47 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2018-11-25 22:12:48 +0100 |
commit | c9fe9fdb61e15b64d03a8da062648ecd3f86d700 (patch) | |
tree | 4c2f6a751aed6a8abe03ef0b65bcbf675bf6bda8 /src/range.c | |
parent | 06f860fd95ba9f97e36416ad8fa3cdadba4f6259 (diff) | |
download | serd-c9fe9fdb61e15b64d03a8da062648ecd3f86d700.tar.gz serd-c9fe9fdb61e15b64d03a8da062648ecd3f86d700.tar.bz2 serd-c9fe9fdb61e15b64d03a8da062648ecd3f86d700.zip |
WIP: Add model
Diffstat (limited to 'src/range.c')
-rw-r--r-- | src/range.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/range.c b/src/range.c new file mode 100644 index 00000000..f83855c1 --- /dev/null +++ b/src/range.c @@ -0,0 +1,248 @@ +/* + 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(const 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 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(const SerdSink* sink, + const unsigned depth, + const SerdStatementFlags flags, + SerdRange* range) +{ + 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(const 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_nodes( + sink, 0, object, rest, nil, serd_statement_get_graph(fs)); + f = NULL; + } + + serd_iter_free(r); + } + + return st; +} + +static SerdStatus +write_range_statement(const 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_BEGIN; + + 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_BEGIN; + 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, + const 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; +} |