Skip to content

Commit

Permalink
Merge pull request #28 from rdf-ext/circular
Browse files Browse the repository at this point in the history
fix: too much recursion error for circular references
  • Loading branch information
bergos authored Aug 5, 2024
2 parents 61f7d89 + 6eafbed commit f545a40
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 2 deletions.
26 changes: 24 additions & 2 deletions lib/Context.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import toNT from '@rdfjs/to-ntriples'
import * as ns from './namespaces.js'
import pathsToString from './pathsToString.js'
import Report from './Report.js'
import Result from './Result.js'

Expand All @@ -7,7 +9,9 @@ class Context {
factory,
focusNode,
options = { debug: false, details: false },
processed = new Set(),
report = new Report({ factory, options }),
results = new Map(),
shape,
value,
valueOrNode,
Expand All @@ -17,7 +21,9 @@ class Context {
this.factory = factory
this.focusNode = focusNode
this.options = options
this.processed = processed
this.report = report
this.results = results
this.shape = shape
this.value = value
this.valuePaths = valuePaths
Expand All @@ -38,7 +44,9 @@ class Context {
factory: this.factory,
focusNode,
options: this.options,
processed: this.processed,
report: child ? new Report({ factory: this.factory, options: this.options }) : this.report,
results: this.results,
shape,
value,
valueOrNode,
Expand All @@ -47,15 +55,29 @@ class Context {
})
}

id ({ shape = this.shape } = {}) {
return `${toNT(shape.ptr.term)} - ${toNT(this.focusNode.term)} - ${pathsToString(this.valuePaths)}`
}

result (args) {
this.report.results.push(new Result({
const result = new Result({
factory: this.factory,
focusNode: this.focusNode,
shape: this.shape,
value: this.value,
valuePaths: this.valuePaths,
...args
}))
})

const id = this.id()

if (!this.results.has(id)) {
this.results.set(id, new Set([result]))
} else {
this.results.get(id).add(result)
}

this.report.results.push(result)
}

debug (constraintComponent, args) {
Expand Down
14 changes: 14 additions & 0 deletions lib/Shape.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ class Shape {
}

async validate (context) {
const id = context.id({ shape: this })

if (context.processed.has(id)) {
if (context.results.has(id)) {
for (const result of context.results.get(id)) {
context.report.results.push(result)
}
}

return context
}

context.processed.add(id)

return this.shapeValidator.validate(context)
}
}
Expand Down
19 changes: 19 additions & 0 deletions lib/pathsToString.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import toNT from '@rdfjs/to-ntriples'

function pathToString (path) {
if (!path) {
return '{}'
}

return `{${[...path.quads()].map(quad => toNT(quad)).join(' ')}}`
}

function pathsToString (paths) {
if (!paths) {
return '{}'
}

return `{${paths.map(path => pathToString(path)).join(' ')}}`
}

export default pathsToString
86 changes: 86 additions & 0 deletions test/assets/custom/circular.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
@prefix ex: <http://example.org/>.
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#>.
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
@prefix sh: <http://www.w3.org/ns/shacl#>.
@prefix shn: <https://schemas.link/shacl-next#>.
@prefix sht: <http://www.w3.org/ns/shacl-test#>.

<> a mf:Manifest;
mf:entries (<circular>).

<circular> a sht:Validate;
rdfs:label "Test of circular references";
mf:action [
sht:dataGraph <>;
sht:shapesGraph <>
];
mf:result [ a sh:ValidationReport;
sh:conforms true;
sh:result [ a sh:ValidationResult;
sh:detail [ a sh:ValidationResult;
sh:detail [ a sh:ValidationResult;
sh:focusNode ex:thing;
sh:resultPath ex:hasPart;
sh:resultSeverity shn:Trace;
sh:sourceConstraintComponent shn:TraversalConstraintComponent;
sh:sourceShape ex:HasPartShape;
sh:value ex:part
], [ a sh:ValidationResult;
sh:focusNode ex:thing;
sh:resultPath ex:hasPart;
sh:resultSeverity shn:Debug;
sh:sourceConstraintComponent sh:NodeConstraintComponent;
sh:sourceShape ex:HasPartShape;
sh:value ex:part
];
sh:focusNode ex:part;
sh:resultPath ex:isPartOf;
sh:resultSeverity shn:Debug;
sh:sourceConstraintComponent sh:OrConstraintComponent;
sh:sourceShape ex:IsPartOfShape;
sh:value ex:thing
], [ a sh:ValidationResult;
sh:focusNode ex:part;
sh:resultPath ex:isPartOf;
sh:resultSeverity shn:Trace;
sh:sourceConstraintComponent shn:TraversalConstraintComponent;
sh:sourceShape ex:IsPartOfShape;
sh:value ex:thing
];
sh:focusNode ex:thing;
sh:resultPath ex:hasPart;
sh:resultSeverity shn:Debug;
sh:sourceConstraintComponent sh:NodeConstraintComponent;
sh:sourceShape ex:HasPartShape;
sh:value ex:part
], [ a sh:ValidationResult;
sh:focusNode ex:thing;
sh:resultPath ex:hasPart;
sh:resultSeverity shn:Trace;
sh:sourceConstraintComponent shn:TraversalConstraintComponent;
sh:sourceShape ex:HasPartShape;
sh:value ex:part
]
].

ex:part
ex:isPartOf ex:thing.

ex:thing a ex:Thing;
ex:hasPart ex:part.

ex:ThingShape a sh:NodeShape;
sh:targetClass ex:Thing;
sh:property ex:HasPartShape.

ex:ThingPartShape a sh:NodeShape;
sh:property ex:IsPartOfShape.

ex:IsPartOfShape
sh:or (ex:ThingShape);
sh:path ex:isPartOf.

ex:HasPartShape
sh:node ex:ThingPartShape;
sh:path ex:hasPart.
1 change: 1 addition & 0 deletions test/assets/custom/manifest.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
rdfs:label "Custom tests";
mf:include
<and-multi-list.ttl>,
<circular.ttl>,
<deactivated-closed.ttl>,
<or-multi-list.ttl>,
<qualifiedValueShape.ttl>,
Expand Down

0 comments on commit f545a40

Please sign in to comment.