From 6408ee7d93ed041a053627e3506c444e215654d4 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 1 Mar 2012 06:39:57 +0000 Subject: Implement Turtle reading (except for atom:Vector). git-svn-id: http://svn.drobilla.net/lad/trunk/sratom@4006 a436a847-0d15-0410-975c-d299462d15a1 --- src/sratom.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 297 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/sratom.c b/src/sratom.c index f919f89..b38b304 100644 --- a/src/sratom.c +++ b/src/sratom.c @@ -23,34 +23,56 @@ #include "sratom/sratom.h" +#define NS_ATOM (const uint8_t*)"http://lv2plug.in/ns/ext/atom#" +#define NS_MIDI (const uint8_t*)"http://lv2plug.in/ns/ext/midi#" #define NS_RDF (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#" #define NS_XSD (const uint8_t*)"http://www.w3.org/2001/XMLSchema#" -#define NS_MIDI (const uint8_t*)"http://lv2plug.in/ns/ext/midi#" #define USTR(str) ((const uint8_t*)(str)) +typedef enum { + MODE_NORMAL, + MODE_SEQUENCE, +} ReadMode; + struct SratomImpl { - SerdWriter* writer; LV2_URID_Map* map; LV2_URID_Unmap* unmap; LV2_Atom_Forge forge; LV2_URID atom_Event; LV2_URID midi_MidiEvent; unsigned next_id; + struct { + SordNode* atom_frameTime; + SordNode* rdf_first; + SordNode* rdf_rest; + SordNode* rdf_type; + SordNode* rdf_value; + } nodes; }; +static void +sratom_read_internal(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node, + ReadMode mode); + SRATOM_API Sratom* sratom_new(LV2_URID_Map* map, LV2_URID_Unmap* unmap) { Sratom* sratom = (Sratom*)malloc(sizeof(Sratom)); - sratom->writer = NULL; sratom->map = map; sratom->unmap = unmap; - sratom->atom_Event = map->map(map->handle, LV2_ATOM_URI "#Event"); - sratom->midi_MidiEvent = map->map(map->handle, (const char*)NS_MIDI "MidiEvent"); + sratom->atom_Event = map->map(map->handle, + (const char*)NS_ATOM "#Event"); + sratom->midi_MidiEvent = map->map(map->handle, + (const char*)NS_MIDI "MidiEvent"); sratom->next_id = 0; + memset(&sratom->nodes, 0, sizeof(sratom->nodes)); lv2_atom_forge_init(&sratom->forge, map); return sratom; } @@ -85,24 +107,24 @@ gensym(SerdNode* out, char c, unsigned num) } static void -list_append(Sratom* sratom, - unsigned* flags, - SerdNode* s, - SerdNode* p, - SerdNode* node, - uint32_t type, - uint32_t size, - void* body) +list_append(Sratom* sratom, + SerdWriter* writer, + unsigned* flags, + SerdNode* s, + SerdNode* p, + SerdNode* node, + uint32_t type, + uint32_t size, + void* body) { // Generate a list node gensym(node, 'l', sratom->next_id); - serd_writer_write_statement(sratom->writer, *flags, NULL, - s, p, node, NULL, NULL); + serd_writer_write_statement(writer, *flags, NULL, s, p, node, NULL, NULL); // _:node rdf:first value *flags = SERD_LIST_CONT; *p = serd_node_from_string(SERD_URI, NS_RDF "first"); - sratom_write(sratom, SERD_LIST_CONT, node, p, type, size, body); + sratom_write(sratom, writer, SERD_LIST_CONT, node, p, type, size, body); // Set subject to node and predicate to rdf:rest for next time gensym(node, 'l', ++sratom->next_id); @@ -121,6 +143,7 @@ list_end(SerdWriter* writer, unsigned* flags, SerdNode* s, SerdNode* p) static void start_object(Sratom* sratom, + SerdWriter* writer, uint32_t flags, const SerdNode* subject, const SerdNode* predicate, @@ -128,13 +151,13 @@ start_object(Sratom* sratom, const char* type) { serd_writer_write_statement( - sratom->writer, flags|SERD_ANON_O_BEGIN, NULL, + writer, flags|SERD_ANON_O_BEGIN, NULL, subject, predicate, node, NULL, NULL); if (type) { SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "type"); SerdNode o = serd_node_from_string(SERD_URI, USTR(type)); serd_writer_write_statement( - sratom->writer, SERD_ANON_CONT, NULL, + writer, SERD_ANON_CONT, NULL, node, &p, &o, NULL, NULL); } } @@ -142,6 +165,7 @@ start_object(Sratom* sratom, SRATOM_API void sratom_write(Sratom* sratom, + SerdWriter* writer, uint32_t flags, const SerdNode* subject, const SerdNode* predicate, @@ -187,7 +211,7 @@ sratom_write(Sratom* sratom, object = serd_node_from_string(SERD_URI, str); } else if (type_urid == sratom->forge.Path) { const uint8_t* str = USTR(body); - object = serd_node_from_string(SERD_LITERAL, str); + object = serd_node_from_string(SERD_LITERAL, str); datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path)); } else if (type_urid == sratom->forge.URI) { const uint8_t* str = USTR(body); @@ -216,45 +240,46 @@ sratom_write(Sratom* sratom, } else if (type_urid == sratom->midi_MidiEvent) { new_node = true; datatype = serd_node_from_string(SERD_URI, NS_MIDI "MidiEvent"); - uint8_t* str = calloc(size * 2, 1); + uint8_t* str = calloc(size * 2 + 1, 1); for (uint32_t i = 0; i < size; ++i) { - sprintf((char*)str + (2 * i), "%02X", - (unsigned)(uint8_t)*((uint8_t*)body + i)); + snprintf((char*)str + (2 * i), size * 2 + 1, "%02X", + (unsigned)(uint8_t)*((uint8_t*)body + i)); } object = serd_node_from_string(SERD_LITERAL, USTR(str)); } else if (type_urid == sratom->atom_Event) { const LV2_Atom_Event* ev = (const LV2_Atom_Event*)body; gensym(&id, 'e', sratom->next_id++); - start_object(sratom, flags, subject, predicate, &id, NULL); + start_object(sratom, writer, flags, subject, predicate, &id, NULL); // TODO: beat time - SerdNode p = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__frameTime)); SerdNode time = serd_node_new_integer(ev->time.frames); + SerdNode p = serd_node_from_string(SERD_URI, + USTR(LV2_ATOM__frameTime)); datatype = serd_node_from_string(SERD_URI, NS_XSD "decimal"); - serd_writer_write_statement(sratom->writer, SERD_ANON_CONT, NULL, + serd_writer_write_statement(writer, SERD_ANON_CONT, NULL, &id, &p, &time, &datatype, &language); serd_node_free(&time); - + p = serd_node_from_string(SERD_URI, NS_RDF "value"); - sratom_write(sratom, SERD_ANON_CONT, &id, &p, + sratom_write(sratom, writer, SERD_ANON_CONT, &id, &p, ev->body.type, ev->body.size, LV2_ATOM_BODY(&ev->body)); - serd_writer_end_anon(sratom->writer, &id); + serd_writer_end_anon(writer, &id); } else if (type_urid == sratom->forge.Tuple) { gensym(&id, 't', sratom->next_id++); - start_object(sratom, flags, subject, predicate, &id, type); + start_object(sratom, writer, flags, subject, predicate, &id, type); SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value"); flags |= SERD_LIST_O_BEGIN; LV2_TUPLE_BODY_FOREACH(body, size, i) { - list_append(sratom, &flags, &id, &p, &node, + list_append(sratom, writer, &flags, &id, &p, &node, i->type, i->size, LV2_ATOM_BODY(i)); } - list_end(sratom->writer, &flags, &id, &p); - serd_writer_end_anon(sratom->writer, &id); + list_end(writer, &flags, &id, &p); + serd_writer_end_anon(writer, &id); } else if (type_urid == sratom->forge.Vector) { const LV2_Atom_Vector_Body* vec = (const LV2_Atom_Vector_Body*)body; gensym(&id, 'v', sratom->next_id++); - start_object(sratom, flags, subject, predicate, &id, type); + start_object(sratom, writer, flags, subject, predicate, &id, type); SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value"); const uint32_t content_size = size - sizeof(LV2_Atom_Vector_Body); const uint32_t elem_size = content_size / vec->elem_count; @@ -262,46 +287,47 @@ sratom_write(Sratom* sratom, for (char* i = (char*)(vec + 1); i < (char*)vec + size; i += elem_size) { - list_append(sratom, &flags, &id, &p, &node, + list_append(sratom, writer, &flags, &id, &p, &node, vec->elem_type, elem_size, i); } - list_end(sratom->writer, &flags, &id, &p); - serd_writer_end_anon(sratom->writer, &id); + list_end(writer, &flags, &id, &p); + serd_writer_end_anon(writer, &id); } else if (type_urid == sratom->forge.Blank) { const LV2_Atom_Object_Body* obj = (const LV2_Atom_Object_Body*)body; - const char* otype = unmap->unmap(unmap->handle, obj->otype); + const char* otype = unmap->unmap(unmap->handle, + obj->otype); gensym(&id, 'b', sratom->next_id++); - start_object(sratom, flags, subject, predicate, &id, otype); + start_object(sratom, writer, flags, subject, predicate, &id, otype); LV2_OBJECT_BODY_FOREACH(obj, size, i) { const LV2_Atom_Property_Body* prop = lv2_object_iter_get(i); const char* const key = unmap->unmap(unmap->handle, prop->key); SerdNode pred = serd_node_from_string(SERD_URI, USTR(key)); - sratom_write(sratom, flags|SERD_ANON_CONT, &id, &pred, + sratom_write(sratom, writer, flags|SERD_ANON_CONT, &id, &pred, prop->value.type, prop->value.size, LV2_ATOM_BODY(&prop->value)); } - serd_writer_end_anon(sratom->writer, &id); + serd_writer_end_anon(writer, &id); } else if (type_urid == sratom->forge.Sequence) { - const LV2_Atom_Sequence_Body* seq = (const LV2_Atom_Sequence_Body*)body; + const LV2_Atom_Sequence_Body* seq = (const LV2_Atom_Sequence_Body*)body; gensym(&id, 'v', sratom->next_id++); - start_object(sratom, flags, subject, predicate, &id, type); + start_object(sratom, writer, flags, subject, predicate, &id, type); SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value"); flags |= SERD_LIST_O_BEGIN; LV2_SEQUENCE_BODY_FOREACH(seq, size, i) { LV2_Atom_Event* ev = lv2_sequence_iter_get(i); - list_append(sratom, &flags, &id, &p, &node, + list_append(sratom, writer, &flags, &id, &p, &node, sratom->atom_Event, sizeof(LV2_Atom_Event) + ev->body.size, ev); } - list_end(sratom->writer, &flags, &id, &p); - serd_writer_end_anon(sratom->writer, &id); + list_end(writer, &flags, &id, &p); + serd_writer_end_anon(writer, &id); } else { object = serd_node_from_string(SERD_LITERAL, USTR("(unknown)")); } if (object.buf) { - serd_writer_write_statement(sratom->writer, flags, NULL, + serd_writer_write_statement(writer, flags, NULL, subject, predicate, &object, &datatype, &language); } @@ -332,17 +358,237 @@ sratom_to_turtle(Sratom* sratom, serd_env_set_prefix_from_strings(env, USTR("eg"), USTR("http://example.org/")); - sratom->writer = serd_writer_new( + SerdWriter* writer = serd_writer_new( SERD_TURTLE, SERD_STYLE_ABBREVIATED|SERD_STYLE_RESOLVED|SERD_STYLE_CURIED, env, &base_uri, string_sink, &str); - sratom_write(sratom, SERD_EMPTY_S, subject, predicate, type, size, body); - serd_writer_finish(sratom->writer); + // Write @prefix directives + serd_env_foreach(env, + (SerdPrefixSink)serd_writer_set_prefix, + writer); + + sratom_write(sratom, writer, SERD_EMPTY_S, + subject, predicate, type, size, body); + serd_writer_finish(writer); string_sink("", 1, &str); - serd_writer_free(sratom->writer); + serd_writer_free(writer); serd_env_free(env); - free(str.buf); return str.buf; } + +static const SordNode* +get_object(SordModel* model, + const SordNode* subject, + const SordNode* predicate) +{ + const SordNode* object = NULL; + SordQuad q = { subject, predicate, 0, 0 }; + SordIter* i = sord_find(model, q); + if (!sord_iter_end(i)) { + SordQuad quad; + sord_iter_get(i, quad); + object = quad[SORD_OBJECT]; + } + sord_iter_free(i); + return object; +} + +static void +read_list_value(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node, + ReadMode mode) +{ + const SordNode* first = get_object(model, node, sratom->nodes.rdf_first); + const SordNode* rest = get_object(model, node, sratom->nodes.rdf_rest); + if (first && rest) { + sratom_read_internal(sratom, forge, world, model, first, mode); + read_list_value(sratom, forge, world, model, rest, mode); + } +} + +static void +sratom_read_internal(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node, + ReadMode mode) +{ + const char* str = (const char*)sord_node_get_string(node); + if (sord_node_get_type(node) == SORD_LITERAL) { + char* endptr; + SordNode* datatype = sord_node_get_datatype(node); + const char* language = sord_node_get_language(node); + if (datatype) { + const char* type_uri = (const char*)sord_node_get_string(datatype); + if (!strcmp(type_uri, (char*)NS_XSD "int")) { + lv2_atom_forge_int32(forge, strtol(str, &endptr, 10)); + } else if (!strcmp(type_uri, (char*)NS_XSD "long")) { + lv2_atom_forge_int64(forge, strtol(str, &endptr, 10)); + } else if (!strcmp(type_uri, (char*)NS_XSD "float")) { + lv2_atom_forge_float(forge, serd_strtod(str, &endptr)); + } else if (!strcmp(type_uri, (char*)NS_XSD "double")) { + lv2_atom_forge_float(forge, serd_strtod(str, &endptr)); + } else if (!strcmp(type_uri, (char*)NS_XSD "boolean")) { + lv2_atom_forge_bool(forge, !strcmp(str, "true")); + } else { + lv2_atom_forge_literal( + forge, (const uint8_t*)str, strlen(str), + sratom->map->map(sratom->map->handle, type_uri), + 0); + } + } else if (language) { + const char* prefix = "http://lexvo.org/id/iso639-3/"; + const size_t lang_len = strlen(prefix) + strlen(language); + char* lang_uri = calloc(lang_len + 1, 1); + snprintf(lang_uri, lang_len + 1, "%s%s", prefix, language); + lv2_atom_forge_literal( + forge, (const uint8_t*)str, strlen(str), 0, + sratom->map->map(sratom->map->handle, lang_uri)); + free(lang_uri); + } else { + lv2_atom_forge_string(forge, (const uint8_t*)str, strlen(str)); + } + } else if (sord_node_get_type(node) == SORD_URI) { + lv2_atom_forge_uri(forge, (const uint8_t*)str, strlen(str)); + } else { + LV2_URID_Map* map = sratom->map; + const SordNode* type = get_object(model, node, sratom->nodes.rdf_type); + + const uint8_t* type_uri = NULL; + uint32_t type_urid = 0; + if (type) { + type_uri = sord_node_get_string(type); + type_urid = map->map(map->handle, (const char*)type_uri); + } + + LV2_Atom_Forge_Frame frame = { 0, 0 }; + if (mode == MODE_SEQUENCE) { + const SordNode* frame_time = get_object( + model, node, sratom->nodes.atom_frameTime); + const SordNode* value = get_object( + model, node, sratom->nodes.rdf_value); + const char* frame_time_str = frame_time + ? (const char*)sord_node_get_string(frame_time) + : ""; + lv2_atom_forge_frame_time(forge, serd_strtod(frame_time_str, NULL)); + sratom_read_internal(sratom, forge, world, model, value, MODE_NORMAL); + } else if (type_urid == sratom->forge.Tuple) { + lv2_atom_forge_tuple(forge, &frame); + const SordNode* value = get_object( + model, node, sratom->nodes.rdf_value); + read_list_value(sratom, forge, world, model, value, MODE_NORMAL); + } else if (type_urid == sratom->forge.Sequence) { + lv2_atom_forge_sequence_head(forge, &frame, 0); + const SordNode* value = get_object( + model, node, sratom->nodes.rdf_value); + read_list_value(sratom, forge, world, model, value, MODE_SEQUENCE); + } else { + lv2_atom_forge_blank(forge, &frame, 1, type_urid); + + SordQuad q = { node, 0, 0, 0 }; + for (SordIter* i = sord_find(model, q); + !sord_iter_end(i); + sord_iter_next(i)) { + SordQuad quad; + sord_iter_get(i, quad); + const SordNode* key = quad[SORD_PREDICATE]; + lv2_atom_forge_property_head( + forge, + map->map(map->handle, (const char*)sord_node_get_string(key)), + 0); + sratom_read_internal(sratom, forge, world, model, quad[SORD_OBJECT], MODE_NORMAL); + } + } + if (frame.ref) { + lv2_atom_forge_pop(forge, &frame); + } + } +} + +SRATOM_API +void +sratom_read(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node) +{ + sratom->nodes.atom_frameTime = sord_new_uri(world, NS_ATOM "frameTime"); + sratom->nodes.rdf_first = sord_new_uri(world, NS_RDF "first"); + sratom->nodes.rdf_rest = sord_new_uri(world, NS_RDF "rest"); + sratom->nodes.rdf_type = sord_new_uri(world, NS_RDF "type"); + sratom->nodes.rdf_value = sord_new_uri(world, NS_RDF "value"); + + sratom_read_internal(sratom, forge, world, model, node, MODE_NORMAL); + + sord_node_free(world, sratom->nodes.rdf_value); + sord_node_free(world, sratom->nodes.rdf_type); + sord_node_free(world, sratom->nodes.rdf_rest); + sord_node_free(world, sratom->nodes.rdf_first); + sord_node_free(world, sratom->nodes.atom_frameTime); + memset(&sratom->nodes, 0, sizeof(sratom->nodes)); +} + +static LV2_Atom_Forge_Ref +forge_sink(LV2_Atom_Forge_Sink_Handle handle, + const void* buf, + uint32_t size) +{ + String* string = (String*)handle; + const LV2_Atom_Forge_Ref ref = string->len; + string_sink(buf, size, string); + return ref; +} + +static void* +forge_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref) +{ + String* string = (String*)handle; + return string->buf + ref; +} + +SRATOM_API +LV2_Atom* +sratom_from_turtle(Sratom* sratom, + const SerdNode* subject, + const SerdNode* predicate, + const char* str) +{ + String out = { NULL, 0 }; + + SerdNode base_uri_node = SERD_NODE_NULL; + SordWorld* world = sord_world_new(); + SordModel* model = sord_new(world, SORD_SPO, false); + SerdEnv* env = serd_env_new(&base_uri_node); + SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); + + if (!serd_reader_read_string(reader, (const uint8_t*)str)) { + SordNode* s = sord_node_from_serd_node(world, env, subject, 0, 0); + SordNode* p = sord_node_from_serd_node(world, env, predicate, 0, 0); + SordQuad q = { s, p, 0, 0 }; + SordIter* i = sord_find(model, q); + if (!sord_iter_end(i)) { + SordQuad result; + sord_iter_get(i, result); + lv2_atom_forge_set_sink(&sratom->forge, forge_sink, forge_deref, &out); + sratom_read(sratom, &sratom->forge, world, model, result[SORD_OBJECT]); + } else { + fprintf(stderr, "Failed to find node\n"); + } + } else { + fprintf(stderr, "Failed to read Turtle\n"); + } + + serd_reader_free(reader); + serd_env_free(env); + sord_free(model); + sord_world_free(world); + + return (LV2_Atom*)out.buf; +} -- cgit v1.2.1