static int check_instances(SordModel* model, const URIs* uris) { int st = 0; SordIter* r = sord_search( model, NULL, uris->rdf_type, uris->owl_Restriction, NULL); for (; !sord_iter_end(r); sord_iter_next(r)) { const SordNode* restriction = sord_iter_get_node(r, SORD_SUBJECT); const SordNode* prop = sord_get( model, restriction, uris->owl_onProperty, NULL, NULL); if (!prop) { continue; } SordIter* c = sord_search( model, NULL, uris->rdfs_subClassOf, restriction, NULL); for (; !sord_iter_end(c); sord_iter_next(c)) { const SordNode* klass = sord_iter_get_node(c, SORD_SUBJECT); check_class_instances(model, uris, restriction, klass); } sord_iter_free(c); } sord_iter_free(r); return st; }
static bool is_descendant_of(SordModel* model, const URIs* uris, const SordNode* child, const SordNode* parent, const SordNode* pred) { if (!child) { return false; } else if (sord_node_equals(child, parent) || sord_ask(model, child, uris->owl_equivalentClass, parent, NULL)) { return true; } SordIter* i = sord_search(model, child, pred, NULL, NULL); for (; !sord_iter_end(i); sord_iter_next(i)) { const SordNode* o = sord_iter_get_node(i, SORD_OBJECT); if (sord_node_equals(child, o)) { continue; // Weird class is explicitly a descendent of itself } if (is_descendant_of(model, uris, o, parent, pred)) { sord_iter_free(i); return true; } } sord_iter_free(i); return false; }
uint64_t sord_count(SordModel* model, const SordNode* s, const SordNode* p, const SordNode* o, const SordNode* g) { SordIter* i = sord_search(model, s, p, o, g); uint64_t n = 0; for (; !sord_iter_end(i); sord_iter_next(i)) { ++n; } sord_iter_free(i); return n; }
static int check_class_instances(SordModel* model, const URIs* uris, const SordNode* restriction, const SordNode* klass) { // Check immediate instances of this class SordIter* i = sord_search(model, NULL, uris->rdf_type, klass, NULL); for (; !sord_iter_end(i); sord_iter_next(i)) { const SordNode* instance = sord_iter_get_node(i, SORD_SUBJECT); check_instance(model, uris, restriction, instance); } sord_iter_free(i); // Check instances of all subclasses recursively SordIter* s = sord_search(model, NULL, uris->rdfs_subClassOf, klass, NULL); for (; !sord_iter_end(s); sord_iter_next(s)) { const SordNode* subklass = sord_iter_get_node(s, SORD_SUBJECT); check_class_instances(model, uris, restriction, subklass); } sord_iter_free(s); return 0; }
static bool check_type(SordModel* model, const URIs* uris, const SordNode* node, const SordNode* type) { if (sord_node_equals(type, uris->rdfs_Resource) || sord_node_equals(type, uris->owl_Thing)) { return true; } if (sord_node_get_type(node) == SORD_LITERAL) { if (sord_node_equals(type, uris->rdfs_Literal)) { return true; } else if (sord_node_equals(type, uris->rdf_PlainLiteral)) { return !sord_node_get_language(node); } else { return literal_is_valid(model, uris, node, type); } } else if (sord_node_get_type(node) == SORD_URI) { if (sord_node_equals(type, uris->foaf_Document)) { return true; // Questionable... } else if (is_descendant_of( model, uris, type, uris->xsd_anyURI, uris->owl_onDatatype)) { /* Type is any URI and this is a URI, so pass. Restrictions on anyURI subtypes are not currently checked (very uncommon). */ return true; // Type is anyURI, and this is a URI } else { SordIter* t = sord_search(model, node, 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), type, uris->rdfs_subClassOf)) { sord_iter_free(t); return true; } } sord_iter_free(t); return false; } } else { return true; // Blanks often lack explicit types, ignore } return false; }
SordNode* sord_get(SordModel* model, const SordNode* s, const SordNode* p, const SordNode* o, const SordNode* g) { if ((bool)s + (bool)p + (bool)o != 2) { return NULL; } SordIter* i = sord_search(model, s, p, o, g); SordNode* ret = NULL; if (!s) { ret = sord_node_copy(sord_iter_get_node(i, SORD_SUBJECT)); } else if (!p) { ret = sord_node_copy(sord_iter_get_node(i, SORD_PREDICATE)); } else if (!o) { ret = sord_node_copy(sord_iter_get_node(i, SORD_OBJECT)); } sord_iter_free(i); return ret; }
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 }
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; }