diff --git a/src/shapes-graph.js b/src/shapes-graph.js index 46e6ef3..270067b 100644 --- a/src/shapes-graph.js +++ b/src/shapes-graph.js @@ -20,7 +20,7 @@ import NodeSet from './node-set.js' import ValidationFunction from './validation-function.js' import validatorsRegistry from './validators-registry.js' import { extractPropertyPath, getPathObjects } from './property-path.js' -import { getInstancesOf, isInstanceOf } from './dataset-utils.js' +import { getInstancesOf, isInstanceOf, rdfListToArray } from './dataset-utils.js' class ShapesGraph { constructor(context) { @@ -124,6 +124,14 @@ class Constraint { get componentMessages() { return this.component.getMessages(this.shape) } + + get nodeSet() { + const { sh } = this.shape.context.ns + if (!this.inNodeSet) { + this.inNodeSet = new NodeSet(rdfListToArray(this.shapeNodePointer.out(sh.in))) + } + return this.inNodeSet + } } class ConstraintComponent { diff --git a/src/validators.js b/src/validators.js index ca376a5..918206d 100644 --- a/src/validators.js +++ b/src/validators.js @@ -131,14 +131,7 @@ function validateHasValueProperty(context, focusNode, valueNode, constraint) { } function validateIn(context, focusNode, valueNode, constraint) { - const { sh } = context.ns - if (!constraint.nodeSet) { - const inNode = constraint.getParameterValue(sh.in) - constraint.nodeSet = new NodeSet(rdfListToArray(context.$shapes.node(inNode))) - } - const { nodeSet } = constraint - - return nodeSet.has(valueNode) + return constraint.nodeSet.has(valueNode) } function validateLanguageIn(context, focusNode, valueNode, constraint) { diff --git a/test/data/validation-sourceShape/mandatory-list.ttl b/test/data/validation-sourceShape/mandatory-list.ttl new file mode 100644 index 0000000..43f6d3a --- /dev/null +++ b/test/data/validation-sourceShape/mandatory-list.ttl @@ -0,0 +1,11 @@ +@prefix sh: . +@prefix ex: . + +ex:p1 a ex:Person . + +ex:shape sh:targetClass ex:Person ; + sh:property [ + sh:path ex:category ; + sh:minCount 1 ; + sh:in (1 2 3) ; + ] . \ No newline at end of file diff --git a/test/validation_sourceShape_test.js b/test/validation_sourceShape_test.js index 691f593..d829371 100644 --- a/test/validation_sourceShape_test.js +++ b/test/validation_sourceShape_test.js @@ -2,8 +2,10 @@ import path from 'path' import assert from 'assert' import * as url from 'url' +import clownface from 'clownface' import SHACLValidator from '../index.js' import ns from '../src/namespaces.js' +import { rdfListToArray } from '../src/dataset-utils.js' import { loadDataset } from './utils.js' const { rdfs, sh } = ns @@ -30,4 +32,21 @@ describe('validation source shapes', () => { const [comment] = report.dataset.match(shape, rdfs.comment, null) assert.strictEqual(comment.object.value, 'sh:in has 5 elements and has been removed from the report for brevity. Please refer the original shape') }) + it('Includes source shape with list', async () => { + const dataPath = path.join(rootPath, 'mandatory-list.ttl') + const data = await loadDataset(dataPath) + const shapes = data + + const validator = new SHACLValidator(shapes) + const report = validator.validate(data) + + assert.strictEqual(report.results.length, 1) + const sourceShapes = new Set(report.results.map(result => result.sourceShape)) + assert.strictEqual(sourceShapes.size, 1) + const [shape] = sourceShapes + // the shape has the lst of allowed values even though the violated constraint is sh:minCount and not sh:in + const list = clownface({ dataset: report.dataset, term: shape }).out(sh.in) + const actual = rdfListToArray(list).map(term => term.value) + assert.deepEqual(actual, ['1', '2', '3']) + }) })