Add ValidationListener documentation (#107)
diff --git a/source/documentation/shacl/__index.md b/source/documentation/shacl/__index.md
index 7030574..a83ead1 100644
--- a/source/documentation/shacl/__index.md
+++ b/source/documentation/shacl/__index.md
@@ -170,3 +170,68 @@
""" ;
] ;
```
+## ValidationListener
+
+When given a `ValidationListener` the SHACL validation code emits events at each step of validation:
+* when validation of a shape starts or finishes
+* when the focus nodes of the shape have been identified
+* when validation of a constraint begins, ends and yields positive or negative results
+
+For example, the following listener will just record all events in a List:
+```
+public class RecordingValidationListener implements ValidationListener {
+ private final List<ValidationEvent> events = new ArrayList<>();
+
+ @Override public void onValidationEvent(ValidationEvent e) {
+ events.add(e);
+ }
+
+ public List<ValidationEvent> getEvents() {
+ return events;
+ }
+ }
+```
+The listener must be passed to the constructor of the `ValidationContext`.
+The following example validates the `dataGraph` according to the `shapesGraph` using the ValidationListener above:
+```
+ Graph shapesGraph = RDFDataMgr.loadGraph(shapesGraphUri); //assuming shapesGraphUri points to an RDF file
+ Graph dataGraph = RDFDataMgr.loadGraph(dataGraphUri); //assuming dataGraphUri points to an RDF file
+ RecordingValidationListener listener = new RecordingValidationListener(); // see above
+ Shapes shapes = Shapes.parse(shapesGraph);
+ ValidationContext vCtx = ValidationContext.create(shapes, dataGraph, listener); // pass listener here
+ for (Shape shape : shapes.getTargetShapes()) {
+ Collection<Node> focusNodes = VLib.focusNodes(dataGraph, shape);
+ for (Node focusNode : focusNodes) {
+ VLib.validateShape(vCtx, dataGraph, shape, focusNode);
+ }
+ }
+ List<ValidationEvent> actualEvents = listener.getEvents(); // all events have been recorded
+```
+
+The events thus generated might look like this (`event.toString()`, one per line):
+```
+ FocusNodeValidationStartedEvent{focusNode=http://datashapes.org/sh/tests/core/node/class-001.test#Someone, shape=NodeShape[http://datashapes.org/sh/tests/core/node/class-001.test#TestShape]}
+ ConstraintEvaluationForNodeShapeStartedEvent{constraint=ClassConstraint[<http://datashapes.org/sh/tests/core/node/class-001.test#Person>], focusNode=http://datashapes.org/sh/tests/core/node/class-001.test#Someone, shape=NodeShape[http://datashapes.org/sh/tests/core/node/class-001.test#TestShape]}
+ ConstraintEvaluatedOnFocusNodeEvent{constraint=ClassConstraint[<http://datashapes.org/sh/tests/core/node/class-001.test#Person>], focusNode=http://datashapes.org/sh/tests/core/node/class-001.test#Someone, shape=NodeShape[http://datashapes.org/sh/tests/core/node/class-001.test#TestShape], valid=true}
+ ConstraintEvaluationForNodeShapeFinishedEvent{constraint=ClassConstraint[<http://datashapes.org/sh/tests/core/node/class-001.test#Person>], focusNode=http://datashapes.org/sh/tests/core/node/class-001.test#Someone, shape=NodeShape[http://datashapes.org/sh/tests/core/node/class-001.test#TestShape]}
+ FocusNodeValidationFinishedEvent{focusNode=http://datashapes.org/sh/tests/core/node/class-001.test#Someone, shape=NodeShape[http://datashapes.org/sh/tests/core/node/class-001.test#TestShape]}
+[...]
+```
+Many use cases can be addressed with the `HandlerBasedValidationListener`, which allows for registering event handlers on a per-event basis.
+For example:
+```
+ ValidationListener myListener = HandlerBasedValidationListener
+ .builder()
+ .forEventType(FocusNodeValidationStartedEvent.class)
+ .addSimpleHandler(e -> {
+ // ...
+ })
+ .forEventType(ConstraintEvaluatedEvent.class)
+ .addHandler(c -> c
+ .iff(EventPredicates.isValid()) // use a Predicate<ValidationEvent> to select events
+ .handle(e -> {
+ // ...
+ })
+ )
+ .build();
+```