/** Compare nodes, considering NULL a wildcard match. */ static inline int sord_node_compare(const SordNode* a, const SordNode* b) { if (a == b || !a || !b) { return 0; // Exact or wildcard match } else if (a->node.type != b->node.type) { return a->node.type - b->node.type; } int cmp = 0; switch (a->node.type) { case SERD_URI: case SERD_BLANK: return strcmp((const char*)a->node.buf, (const char*)b->node.buf); case SERD_LITERAL: cmp = strcmp((const char*)sord_node_get_string(a), (const char*)sord_node_get_string(b)); if (cmp == 0) { // Note: Can't use sord_node_compare here since it does wildcards if (!a->meta.lit.datatype || !b->meta.lit.datatype) { cmp = a->meta.lit.datatype - b->meta.lit.datatype; } else { cmp = strcmp((const char*)a->meta.lit.datatype->node.buf, (const char*)b->meta.lit.datatype->node.buf); } } if (cmp == 0) { cmp = strcmp(a->meta.lit.lang, b->meta.lit.lang); } default: break; } return cmp; }
static int error(const char* msg, const SordQuad quad) { const char* sep = one_line_errors ? "\t" : "\n "; ++n_errors; fprintf(stderr, "error: %s:%s%s%s%s%s%s\n", msg, sep, (const char*)sord_node_get_string(quad[SORD_SUBJECT]), sep, (const char*)sord_node_get_string(quad[SORD_PREDICATE]), sep, (const char*)sord_node_get_string(quad[SORD_OBJECT])); return 1; }
/** Create a new LilvNode from `node`, or return NULL if impossible */ LilvNode* lilv_node_new_from_node(LilvWorld* world, const SordNode* node) { if (!node) { return NULL; } LilvNode* result = NULL; SordNode* datatype_uri = NULL; LilvNodeType type = LILV_VALUE_STRING; switch (sord_node_get_type(node)) { case SORD_URI: result = (LilvNode*)malloc(sizeof(LilvNode)); result->world = (LilvWorld*)world; result->type = LILV_VALUE_URI; result->node = sord_node_copy(node); break; case SORD_BLANK: result = (LilvNode*)malloc(sizeof(LilvNode)); result->world = (LilvWorld*)world; result->type = LILV_VALUE_BLANK; result->node = sord_node_copy(node); break; case SORD_LITERAL: datatype_uri = sord_node_get_datatype(node); if (datatype_uri) { if (sord_node_equals(datatype_uri, world->uris.xsd_boolean)) type = LILV_VALUE_BOOL; else if (sord_node_equals(datatype_uri, world->uris.xsd_decimal) || sord_node_equals(datatype_uri, world->uris.xsd_double)) type = LILV_VALUE_FLOAT; else if (sord_node_equals(datatype_uri, world->uris.xsd_integer)) type = LILV_VALUE_INT; else if (sord_node_equals(datatype_uri, world->uris.xsd_base64Binary)) type = LILV_VALUE_BLOB; else LILV_ERRORF("Unknown datatype `%s'\n", sord_node_get_string(datatype_uri)); } result = lilv_node_new( world, type, (const char*)sord_node_get_string(node)); lilv_node_set_numerics_from_string(result); break; } return result; }
LILV_API const char* lilv_node_as_blank(const LilvNode* value) { return (lilv_node_is_blank(value) ? (const char*)sord_node_get_string(value->node) : NULL); }
static void lilv_node_set_numerics_from_string(LilvNode* val) { const char* str = (const char*)sord_node_get_string(val->node); switch (val->type) { case LILV_VALUE_URI: case LILV_VALUE_BLANK: case LILV_VALUE_STRING: case LILV_VALUE_BLOB: break; case LILV_VALUE_INT: val->val.int_val = strtol(str, NULL, 10); break; case LILV_VALUE_FLOAT: val->val.float_val = serd_strtod(str, NULL); break; case LILV_VALUE_BOOL: val->val.bool_val = !strcmp(str, "true"); break; } }
LILV_API char* lilv_node_get_turtle_token(const LilvNode* value) { const char* str = (const char*)sord_node_get_string(value->node); size_t len = 0; char* result = NULL; SerdNode node; switch (value->type) { case LILV_VALUE_URI: len = strlen(str) + 3; result = (char*)calloc(len, 1); snprintf(result, len, "<%s>", str); break; case LILV_VALUE_BLANK: len = strlen(str) + 3; result = (char*)calloc(len, 1); snprintf(result, len, "_:%s", str); break; case LILV_VALUE_STRING: case LILV_VALUE_BOOL: case LILV_VALUE_BLOB: result = lilv_strdup(str); break; case LILV_VALUE_INT: node = serd_node_new_integer(value->val.int_val); result = (char*)node.buf; break; case LILV_VALUE_FLOAT: node = serd_node_new_decimal(value->val.float_val, 8); result = (char*)node.buf; break; } return result; }
static int check_instance(SordModel* model, const URIs* uris, const SordNode* restriction, const SordNode* instance) { int st = 0; const SordNode* prop = sord_get( model, restriction, uris->owl_onProperty, NULL, NULL); if (!prop) { return 0; } const unsigned values = sord_count(model, instance, prop, NULL, NULL); // Check exact cardinality const SordNode* card = sord_get( model, restriction, uris->owl_cardinality, NULL, NULL); if (card) { const unsigned c = atoi((const char*)sord_node_get_string(card)); if (values != c) { st = errorf("Property %s on %s has %u != %u values\n", sord_node_get_string(prop), sord_node_get_string(instance), values, c); } } // Check minimum cardinality const SordNode* minCard = sord_get( model, restriction, uris->owl_minCardinality, NULL, NULL); if (minCard) { const unsigned m = atoi((const char*)sord_node_get_string(minCard)); if (values < m) { st = errorf("Property %s on %s has %u < %u values\n", sord_node_get_string(prop), sord_node_get_string(instance), values, m); } } // Check someValuesFrom SordIter* sf = sord_search( model, restriction, uris->owl_someValuesFrom, NULL, NULL); if (sf) { const SordNode* type = sord_iter_get_node(sf, SORD_OBJECT); SordIter* v = sord_search(model, instance, prop, NULL, NULL); bool found = false; for (; !sord_iter_end(v); sord_iter_next(v)) { const SordNode* value = sord_iter_get_node(v, SORD_OBJECT); if (check_type(model, uris, value, type)) { found = true; break; } } if (!found) { st = errorf("%s has no <%s> values of type <%s>\n", sord_node_get_string(instance), sord_node_get_string(prop), sord_node_get_string(type)); } sord_iter_free(v); } sord_iter_free(sf); return st; }
static int check_properties(SordModel* model, URIs* uris) { int st = 0; SordIter* i = sord_begin(model); for (; !sord_iter_end(i); sord_iter_next(i)) { SordQuad quad; sord_iter_get(i, quad); const SordNode* subj = quad[SORD_SUBJECT]; const SordNode* pred = quad[SORD_PREDICATE]; const SordNode* obj = quad[SORD_OBJECT]; bool is_any_property = false; SordIter* t = sord_search(model, pred, uris->rdf_type, NULL, NULL); for (; !sord_iter_end(t); sord_iter_next(t)) { if (is_descendant_of(model, uris, sord_iter_get_node(t, SORD_OBJECT), uris->rdf_Property, uris->rdfs_subClassOf)) { is_any_property = true; break; } } sord_iter_free(t); const bool is_ObjectProperty = sord_ask( model, pred, uris->rdf_type, uris->owl_ObjectProperty, 0); const bool is_FunctionalProperty = sord_ask( model, pred, uris->rdf_type, uris->owl_FunctionalProperty, 0); const bool is_InverseFunctionalProperty = sord_ask( model, pred, uris->rdf_type, uris->owl_InverseFunctionalProperty, 0); const bool is_DatatypeProperty = sord_ask( model, pred, uris->rdf_type, uris->owl_DatatypeProperty, 0); if (!is_any_property) { st = error("Use of undefined property", quad); } if (!sord_ask(model, pred, uris->rdfs_label, NULL, NULL)) { st = errorf("Property <%s> has no label\n", sord_node_get_string(pred)); } if (is_DatatypeProperty && sord_node_get_type(obj) != SORD_LITERAL) { st = error("Datatype property with non-literal value", quad); } if (is_ObjectProperty && sord_node_get_type(obj) == SORD_LITERAL) { st = error("Object property with literal value", quad); } if (is_FunctionalProperty && sord_count(model, subj, pred, NULL, NULL) > 1) { st = error("Functional property with several objects", quad); } if (is_InverseFunctionalProperty && sord_count(model, NULL, pred, obj, NULL) > 1) { st = error("Inverse functional property with several subjects", quad); } if (sord_node_equals(pred, uris->rdf_type) && !sord_ask(model, obj, uris->rdf_type, uris->rdfs_Class, NULL) && !sord_ask(model, obj, uris->rdf_type, uris->owl_Class, NULL)) { st = error("Type is not a rdfs:Class or owl:Class", quad); } if (sord_node_get_type(obj) == SORD_LITERAL && !literal_is_valid(model, uris, obj, sord_node_get_datatype(obj))) { st = error("Literal does not match datatype", quad); } SordIter* r = sord_search(model, pred, uris->rdfs_range, NULL, NULL); for (; !sord_iter_end(r); sord_iter_next(r)) { const SordNode* range = sord_iter_get_node(r, SORD_OBJECT); if (!check_type(model, uris, obj, range)) { st = error("Object not in property range", quad); fprintf(stderr, "note: Range is <%s>\n", sord_node_get_string(range)); } } sord_iter_free(r); SordIter* d = sord_search(model, pred, uris->rdfs_domain, NULL, NULL); if (d) { const SordNode* domain = sord_iter_get_node(d, SORD_OBJECT); if (!check_type(model, uris, subj, domain)) { st = error("Subject not in property domain", quad); fprintf(stderr, "note: Domain is <%s>\n", sord_node_get_string(domain)); } sord_iter_free(d); } } sord_iter_free(i); return st; }
static bool literal_is_valid(SordModel* model, const URIs* uris, const SordNode* literal, const SordNode* type) { if (!type) { return true; } /* Check that literal data is related to required type. We don't do a strict subtype check here because e.g. an xsd:decimal might be a valid xsd:unsignedInt, which the pattern checks will verify, but if the literal type is not related to the required type at all (e.g. xsd:decimal and xsd:string) there is a problem. */ const SordNode* datatype = sord_node_get_datatype(literal); if (datatype && datatype != type) { if (!is_descendant_of( model, uris, datatype, type, uris->owl_onDatatype) && !is_descendant_of( model, uris, type, datatype, uris->owl_onDatatype)) { errorf("Literal `%s' datatype <%s> is not compatible with <%s>\n", sord_node_get_string(literal), sord_node_get_string(datatype), sord_node_get_string(type)); return false; } } // Find restrictions list SordIter* rs = sord_search(model, type, uris->owl_withRestrictions, 0, 0); if (sord_iter_end(rs)) { return true; // No restrictions } // Walk list, checking each restriction const SordNode* head = sord_iter_get_node(rs, SORD_OBJECT); while (head) { SordIter* f = sord_search(model, head, uris->rdf_first, 0, 0); if (!f) { break; // Reached end of restrictions list without failure } // Check this restriction const bool good = check_restriction( model, uris, literal, type, sord_iter_get_node(f, SORD_OBJECT)); sord_iter_free(f); if (!good) { sord_iter_free(rs); return false; // Failed, literal is invalid } // Seek to next list node SordIter* n = sord_search(model, head, uris->rdf_rest, 0, 0); head = n ? sord_iter_get_node(n, SORD_OBJECT) : NULL; sord_iter_free(n); } sord_iter_free(rs); SordIter* s = sord_search(model, type, uris->owl_onDatatype, 0, 0); if (s) { const SordNode* super = sord_iter_get_node(s, SORD_OBJECT); const bool good = literal_is_valid(model, uris, literal, super); sord_iter_free(s); return good; // Match iff literal also matches supertype } return true; // Matches top level type }
static bool check_restriction(SordModel* model, const URIs* uris, const SordNode* literal, const SordNode* type, const SordNode* restriction) { size_t len = 0; const char* str = (const char*)sord_node_get_string_counted(literal, &len); ++n_restrictions; // Check xsd:pattern SordIter* p = sord_search(model, restriction, uris->xsd_pattern, 0, 0); if (p) { const SordNode* pat = sord_iter_get_node(p, SORD_OBJECT); const bool good = regexp_match(sord_node_get_string(pat), str); if (!good) { fprintf(stderr, "`%s' does not match <%s> pattern `%s'\n", sord_node_get_string(literal), sord_node_get_string(type), sord_node_get_string(pat)); } sord_iter_free(p); return good; } /* We'll do some comparison tricks for xsd:decimal types, where lexicographical comparison would be incorrect. Note that if the literal's type is a descendant of xsd:decimal, we'll end up checking it against the xsd:decimal pattern so there's no need to validate digits here. At worst we'll get a false positive but it will fail later. */ const bool is_decimal = is_descendant_of( model, uris, type, uris->xsd_decimal, uris->owl_onDatatype); // Check xsd:minInclusive SordIter* l = sord_search(model, restriction, uris->xsd_minInclusive, 0, 0); if (l) { const SordNode* lower = sord_iter_get_node(l, SORD_OBJECT); size_t lower_len = 0; const char* lower_str = (const char*)sord_node_get_string_counted(lower, &lower_len); bool good = false; if (!is_decimal || len == lower_len) { // Not decimal, or equal lengths, strcmp good = (strcmp(str, lower_str) >= 0); } else { // Decimal with different length, only good if longer than the min good = (len > lower_len); } if (!good) { fprintf(stderr, "`%s' is not >= <%s> minimum `%s'\n", sord_node_get_string(literal), sord_node_get_string(type), sord_node_get_string(lower)); } sord_iter_free(l); return good; } // Check xsd:maxInclusive SordIter* u = sord_search(model, restriction, uris->xsd_maxInclusive, 0, 0); if (u) { const SordNode* upper = sord_iter_get_node(u, SORD_OBJECT); size_t upper_len = 0; const char* upper_str = (const char*)sord_node_get_string_counted(upper, &upper_len); bool good = false; if (!is_decimal || len == upper_len) { // Not decimal, or equal lengths, strcmp good = (strcmp(str, upper_str) <= 0); } else { // Decimal with different length, only good if shorter than the max good = (len < upper_len); } if (!good) { fprintf(stderr, "`%s' is not <= <%s> maximum `%s'\n", sord_node_get_string(literal), sord_node_get_string(type), sord_node_get_string(upper)); } sord_iter_free(u); return good; } --n_restrictions; return true; // Unknown restriction, be quietly tolerant }
LILV_API const char* lilv_node_as_string(const LilvNode* value) { return value ? (const char*)sord_node_get_string(value->node) : NULL; }
static LilvState* new_state_from_model(LilvWorld* world, LV2_URID_Map* map, SordModel* model, const SordNode* node, const char* dir) { LilvState* const state = (LilvState*)malloc(sizeof(LilvState)); memset(state, '\0', sizeof(LilvState)); state->dir = lilv_strdup(dir); state->atom_Path = map->map(map->handle, LV2_ATOM__Path); // Get the plugin URI this state applies to SordIter* i = sord_search(model, node, world->uris.lv2_appliesTo, 0, 0); if (i) { const SordNode* object = sord_iter_get_node(i, SORD_OBJECT); const SordNode* graph = sord_iter_get_node(i, SORD_GRAPH); state->plugin_uri = lilv_node_new_from_node(world, object); if (!state->dir && graph) { state->dir = lilv_strdup((const char*)sord_node_get_string(graph)); } sord_iter_free(i); } else if (sord_ask(model, node, world->uris.rdf_a, world->uris.lv2_Plugin, 0)) { // Loading plugin description as state (default state) state->plugin_uri = lilv_node_new_from_node(world, node); } else { LILV_ERRORF("State %s missing lv2:appliesTo property\n", sord_node_get_string(node)); } // Get the state label i = sord_search(model, node, world->uris.rdfs_label, NULL, NULL); if (i) { const SordNode* object = sord_iter_get_node(i, SORD_OBJECT); const SordNode* graph = sord_iter_get_node(i, SORD_GRAPH); state->label = lilv_strdup((const char*)sord_node_get_string(object)); if (!state->dir) { state->dir = lilv_strdup((const char*)sord_node_get_string(graph)); } sord_iter_free(i); } Sratom* sratom = sratom_new(map); SerdChunk chunk = { NULL, 0 }; LV2_Atom_Forge forge; lv2_atom_forge_init(&forge, map); lv2_atom_forge_set_sink( &forge, sratom_forge_sink, sratom_forge_deref, &chunk); // Get port values SordIter* ports = sord_search(model, node, world->uris.lv2_port, 0, 0); FOREACH_MATCH(ports) { const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT); SordNode* label = sord_get(model, port, world->uris.rdfs_label, 0, 0); SordNode* symbol = sord_get(model, port, world->uris.lv2_symbol, 0, 0); SordNode* value = sord_get(model, port, world->uris.pset_value, 0, 0); if (!value) { value = sord_get(model, port, world->uris.lv2_default, 0, 0); } if (!symbol) { LILV_ERRORF("State `%s' port missing symbol.\n", sord_node_get_string(node)); } else if (value) { chunk.len = 0; sratom_read(sratom, &forge, world->world, model, value); LV2_Atom* atom = (LV2_Atom*)chunk.buf; append_port_value(state, (const char*)sord_node_get_string(symbol), LV2_ATOM_BODY(atom), atom->size, atom->type); if (label) { lilv_state_set_label(state, (const char*)sord_node_get_string(label)); } } sord_node_free(world->world, value); sord_node_free(world->world, symbol); sord_node_free(world->world, label); } sord_iter_free(ports); // Get properties SordNode* statep = sord_new_uri(world->world, USTR(LV2_STATE__state)); SordNode* state_node = sord_get(model, node, statep, NULL, NULL); if (state_node) { SordIter* props = sord_search(model, state_node, 0, 0, 0); FOREACH_MATCH(props) { const SordNode* p = sord_iter_get_node(props, SORD_PREDICATE); const SordNode* o = sord_iter_get_node(props, SORD_OBJECT); chunk.len = 0; lv2_atom_forge_set_sink( &forge, sratom_forge_sink, sratom_forge_deref, &chunk); sratom_read(sratom, &forge, world->world, model, o); LV2_Atom* atom = (LV2_Atom*)chunk.buf; uint32_t flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; Property prop = { NULL, 0, 0, 0, flags }; prop.key = map->map(map->handle, (const char*)sord_node_get_string(p)); prop.type = atom->type; prop.size = atom->size; prop.value = malloc(atom->size); memcpy(prop.value, LV2_ATOM_BODY(atom), atom->size); if (atom->type == forge.Path) { prop.flags = LV2_STATE_IS_PORTABLE; } if (prop.value) { state->props = (Property*)realloc( state->props, (++state->num_props) * sizeof(Property)); state->props[state->num_props - 1] = prop; } } sord_iter_free(props); } sord_node_free(world->world, state_node); sord_node_free(world->world, statep); free((void*)chunk.buf); sratom_free(sratom); qsort(state->props, state->num_props, sizeof(Property), property_cmp); qsort(state->values, state->num_values, sizeof(PortValue), value_cmp); return state; }