Skip to content

Commit 5137bf2

Browse files
committed
fixes based on review
1 parent f5e5909 commit 5137bf2

2 files changed

Lines changed: 100 additions & 1 deletion

File tree

core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ShaclSailConnection.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1344,7 +1344,8 @@ public void prepare() throws SailException {
13441344
: "isShapeRefreshNeeded should trigger shapesModifiedInCurrentTransaction once we have loaded the modified shapes, but shapesModifiedInCurrentTransaction should be null until then";
13451345

13461346
if (!shapeRefreshNeeded && !isBulkValidation() && addedStatementsSet.isEmpty()
1347-
&& removedStatementsSet.isEmpty()) {
1347+
&& removedStatementsSet.isEmpty() && addedStatementsInferredSet.isEmpty()
1348+
&& removedStatementsInferredSet.isEmpty()) {
13481349
logger.debug("Nothing has changed, nothing to validate.");
13491350
return;
13501351
}

core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/RdfsReasoningShaclSailTest.java

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,28 @@ public void inferredTargetClassChangeIsDetectedWhenInferredStatementsReported()
131131
}
132132
}
133133

134+
@Test
135+
public void inferredOnlyNotificationsStillTriggerValidation() throws Exception {
136+
SailRepository repo = createRepositoryWithExplicitStatementFiltering(true);
137+
138+
IRI ontologyGraph = repo.getValueFactory().createIRI("urn:ontology");
139+
IRI dataGraph = repo.getValueFactory().createIRI("urn:data");
140+
141+
loadTurtle(repo, ontologyTurtle(), ontologyGraph);
142+
loadTurtle(repo, railcarBrakeShapeTurtle(null, null), RDF4J.SHACL_SHAPE_GRAPH);
143+
144+
IRI wagon1 = repo.getValueFactory().createIRI("urn:wagon1");
145+
IRI freightWagon = repo.getValueFactory().createIRI("https://example.com/trains/FreightWagon");
146+
147+
try (RepositoryConnection conn = repo.getConnection()) {
148+
conn.begin();
149+
conn.add(wagon1, RDF.TYPE, freightWagon, dataGraph);
150+
assertThrows(ShaclSailValidationException.class, () -> commitAndRethrow(conn));
151+
} finally {
152+
repo.shutDown();
153+
}
154+
}
155+
134156
@Test
135157
public void targetClassUsesPerShapeRdfsSubClassReasoning() throws Exception {
136158
SailRepository repo = createRepository(false, false, false);
@@ -279,6 +301,20 @@ private static SailRepository createRepositoryWithLegacyCallbackForwarding(boole
279301
return repo;
280302
}
281303

304+
private static SailRepository createRepositoryWithExplicitStatementFiltering(boolean includeInferredStatements) {
305+
NotifyingSail baseSail = new ExplicitStatementFilteringSail(new MemoryStore());
306+
NotifyingSail shaclBase = new SchemaCachingRDFSInferencer(baseSail);
307+
ShaclSail shaclSail = new ShaclSail(shaclBase);
308+
shaclSail.setRdfsSubClassReasoning(false);
309+
shaclSail.setIncludeInferredStatements(includeInferredStatements);
310+
shaclSail.setEclipseRdf4jShaclExtensions(true);
311+
shaclSail.setSerializableValidation(false);
312+
313+
SailRepository repo = new SailRepository(shaclSail);
314+
repo.init();
315+
return repo;
316+
}
317+
282318
private static SailRepository createRepository(boolean includeInferredStatements,
283319
boolean filterInferredNotifications,
284320
boolean useSchemaCachingInferencer) {
@@ -469,6 +505,68 @@ public void removeConnectionListener(SailConnectionListener listener) {
469505
}
470506
}
471507

508+
// Test helper: hide explicit change notifications from listeners.
509+
private static final class ExplicitStatementFilteringSail extends NotifyingSailWrapper {
510+
ExplicitStatementFilteringSail(NotifyingSail baseSail) {
511+
super(baseSail);
512+
}
513+
514+
@Override
515+
public NotifyingSailConnection getConnection() throws SailException {
516+
InferencerConnection connection = (InferencerConnection) super.getConnection();
517+
return new ExplicitStatementFilteringConnection(connection);
518+
}
519+
}
520+
521+
private static final class ExplicitStatementFilteringConnection extends InferencerConnectionWrapper {
522+
private final Map<SailConnectionListener, SailConnectionListener> listenerMap = new IdentityHashMap<>();
523+
524+
ExplicitStatementFilteringConnection(InferencerConnection wrappedCon) {
525+
super(wrappedCon);
526+
}
527+
528+
@Override
529+
public void addConnectionListener(SailConnectionListener listener) {
530+
SailConnectionListener filteringListener = new SailConnectionListener() {
531+
@Override
532+
public void statementAdded(Statement st) {
533+
statementAdded(st, false);
534+
}
535+
536+
@Override
537+
public void statementRemoved(Statement st) {
538+
statementRemoved(st, false);
539+
}
540+
541+
@Override
542+
public void statementAdded(Statement st, boolean inferred) {
543+
if (inferred) {
544+
listener.statementAdded(st, true);
545+
}
546+
}
547+
548+
@Override
549+
public void statementRemoved(Statement st, boolean inferred) {
550+
if (inferred) {
551+
listener.statementRemoved(st, true);
552+
}
553+
}
554+
};
555+
listenerMap.put(listener, filteringListener);
556+
super.addConnectionListener(filteringListener);
557+
}
558+
559+
@Override
560+
public void removeConnectionListener(SailConnectionListener listener) {
561+
SailConnectionListener filteringListener = listenerMap.remove(listener);
562+
if (filteringListener != null) {
563+
super.removeConnectionListener(filteringListener);
564+
} else {
565+
super.removeConnectionListener(listener);
566+
}
567+
}
568+
}
569+
472570
// Test helper: force all notifications through deprecated no-flag listener methods.
473571
private static final class LegacyCallbackForwardingSail extends NotifyingSailWrapper {
474572
LegacyCallbackForwardingSail(NotifyingSail baseSail) {

0 commit comments

Comments
 (0)