rdf-validate-shacl
Version:
RDF SHACL validator
85 lines (84 loc) • 2.98 kB
JavaScript
import TermSet from '@rdfjs/term-set';
import NodeSet from './node-set.js';
/**
* Extracts all the quads forming the structure under a blank node. Stops at
* non-blank nodes.
*/
export function* extractStructure(dataset, startNode, visited = new TermSet()) {
if (startNode.termType !== 'BlankNode' || visited.has(startNode)) {
return;
}
visited.add(startNode);
for (const quad of dataset.match(startNode, null, null)) {
yield quad;
yield* extractStructure(dataset, quad.object, visited);
}
}
/**
* Extracts all the quads forming the structure under a blank shape node. Stops at
* non-blank nodes. Replaces sh:in with a comment if the list is too long.
*/
export function* extractSourceShapeStructure(shape, dataset, startNode, visited = new TermSet()) {
if (startNode.termType !== 'BlankNode' || visited.has(startNode)) {
return;
}
const { factory } = shape.context;
const { sh, rdfs } = shape.context.ns;
const inListSize = (term) => {
const inConstraint = shape.constraints.find(x => term.equals(x.paramValue));
return inConstraint?.nodeSet.size || -1;
};
visited.add(startNode);
for (const quad of dataset.match(startNode, null, null)) {
if (quad.predicate.equals(sh.in) && inListSize(quad.object) > 3) {
const msg = `sh:in has ${inListSize(quad.object)} elements and has been removed from the report for brevity. Please refer the original shape`;
yield factory.quad(quad.subject, rdfs.comment, factory.literal(msg));
}
else {
yield quad;
yield* extractSourceShapeStructure(shape, dataset, quad.object, visited);
}
}
}
/**
* Get instances of a class.
*/
export function getInstancesOf(cls, ns) {
const classes = getSubClassesOf(cls, ns);
classes.add(cls.term);
return [...classes].reduce((acc, classTerm) => {
const classInstances = cls
.node(classTerm)
.in(ns.rdf.type)
.terms;
acc.addAll(classInstances);
return acc;
}, new NodeSet());
}
/**
* Get subclasses of a class.
*/
export function getSubClassesOf(cls, ns) {
const subclasses = cls.in(ns.rdfs.subClassOf);
const transubclasses = subclasses.toArray().reduce((acc, subclass) => {
const scs = getSubClassesOf(subclass, ns);
acc.addAll(scs);
return acc;
}, new NodeSet());
return new NodeSet([...subclasses.terms, ...transubclasses]);
}
/**
* Check if a node is an instance of a class.
*/
export function isInstanceOf(instance, cls, ns) {
const classes = getSubClassesOf(cls, ns);
classes.add(cls.term);
const types = instance.out(ns.rdf.type).terms;
return types.some((type) => classes.has(type));
}
/**
* Extract all the terms of an RDF-list and return then as an array.
*/
export function rdfListToArray(listNode) {
return [...listNode.list?.() || []].map(({ term }) => term);
}