aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-06-28 21:21:53 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-06-28 21:44:58 +0200
commitb52a4b0b330c4f4903fb4aa41a63980774b43c85 (patch)
treee0a0f90580d6b571572d4aab173b09e66c96ef94 /subprojects
parentrefactor: improve propagation traceability (diff)
downloadrefinery-b52a4b0b330c4f4903fb4aa41a63980774b43c85.tar.gz
refinery-b52a4b0b330c4f4903fb4aa41a63980774b43c85.tar.zst
refinery-b52a4b0b330c4f4903fb4aa41a63980774b43c85.zip
refactor: improve propagation rule diagnosticsHEADmain
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java3
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java1
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTrace.java4
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java24
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java67
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java2
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationRejectedResult.java8
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java10
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java9
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java10
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java3
11 files changed, 104 insertions, 37 deletions
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java b/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java
index 77f05a9d..3c3488df 100644
--- a/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java
+++ b/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java
@@ -41,7 +41,8 @@ public final class ModelSemanticsFactory {
41 var storeBuilder = ModelStore.builder() 41 var storeBuilder = ModelStore.builder()
42 .cancellationToken(cancellationToken) 42 .cancellationToken(cancellationToken)
43 .with(QueryInterpreterAdapter.builder()) 43 .with(QueryInterpreterAdapter.builder())
44 .with(PropagationAdapter.builder()) 44 .with(PropagationAdapter.builder()
45 .throwOnFatalRejection(false))
45 .with(ReasoningAdapter.builder() 46 .with(ReasoningAdapter.builder()
46 .requiredInterpretations(Set.of(Concreteness.PARTIAL))); 47 .requiredInterpretations(Set.of(Concreteness.PARTIAL)));
47 initializer.configureStoreBuilder(storeBuilder); 48 initializer.configureStoreBuilder(storeBuilder);
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
index c2243f08..8bfc8acd 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
@@ -967,6 +967,7 @@ public class ModelInitializer {
967 .orElseGet(() -> "::rule" + ruleCount); 967 .orElseGet(() -> "::rule" + ruleCount);
968 ruleCount++; 968 ruleCount++;
969 var rule = toRule(name, ruleDefinition); 969 var rule = toRule(name, ruleDefinition);
970 problemTrace.putRuleDefinition(ruleDefinition, rule);
970 switch (ruleDefinition.getKind()) { 971 switch (ruleDefinition.getKind()) {
971 case DECISION -> storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class) 972 case DECISION -> storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class)
972 .ifPresent(dseBuilder -> dseBuilder.transformation(rule)); 973 .ifPresent(dseBuilder -> dseBuilder.transformation(rule));
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTrace.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTrace.java
index b8d0b804..9122cf1f 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTrace.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTrace.java
@@ -10,6 +10,8 @@ import org.eclipse.xtext.naming.QualifiedName;
10import tools.refinery.language.model.problem.Node; 10import tools.refinery.language.model.problem.Node;
11import tools.refinery.language.model.problem.Problem; 11import tools.refinery.language.model.problem.Problem;
12import tools.refinery.language.model.problem.Relation; 12import tools.refinery.language.model.problem.Relation;
13import tools.refinery.language.model.problem.RuleDefinition;
14import tools.refinery.store.dse.transition.Rule;
13import tools.refinery.store.reasoning.representation.AnyPartialSymbol; 15import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
14import tools.refinery.store.reasoning.representation.PartialRelation; 16import tools.refinery.store.reasoning.representation.PartialRelation;
15import tools.refinery.store.reasoning.translator.TranslationException; 17import tools.refinery.store.reasoning.translator.TranslationException;
@@ -36,6 +38,8 @@ public interface ProblemTrace {
36 38
37 Relation getRelation(AnyPartialSymbol partialSymbol); 39 Relation getRelation(AnyPartialSymbol partialSymbol);
38 40
41 RuleDefinition getRuleDefinition(Rule rule);
42
39 RuntimeException wrapException(TranslationException translationException); 43 RuntimeException wrapException(TranslationException translationException);
40 44
41 PartialRelation getPartialRelation(Relation relation); 45 PartialRelation getPartialRelation(Relation relation);
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java
index 4b44339c..5584af10 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java
@@ -13,10 +13,8 @@ import org.eclipse.xtext.naming.IQualifiedNameConverter;
13import org.eclipse.xtext.naming.QualifiedName; 13import org.eclipse.xtext.naming.QualifiedName;
14import org.eclipse.xtext.scoping.IScope; 14import org.eclipse.xtext.scoping.IScope;
15import org.eclipse.xtext.scoping.IScopeProvider; 15import org.eclipse.xtext.scoping.IScopeProvider;
16import tools.refinery.language.model.problem.Node; 16import tools.refinery.language.model.problem.*;
17import tools.refinery.language.model.problem.Problem; 17import tools.refinery.store.dse.transition.Rule;
18import tools.refinery.language.model.problem.ProblemPackage;
19import tools.refinery.language.model.problem.Relation;
20import tools.refinery.store.reasoning.representation.AnyPartialSymbol; 18import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
21import tools.refinery.store.reasoning.representation.PartialRelation; 19import tools.refinery.store.reasoning.representation.PartialRelation;
22import tools.refinery.store.reasoning.translator.TranslationException; 20import tools.refinery.store.reasoning.translator.TranslationException;
@@ -46,6 +44,7 @@ class ProblemTraceImpl implements ProblemTrace {
46 Collections.unmodifiableMap(mutableRelationTrace); 44 Collections.unmodifiableMap(mutableRelationTrace);
47 private final Map<AnyPartialSymbol, Relation> mutableInverseTrace = new HashMap<>(); 45 private final Map<AnyPartialSymbol, Relation> mutableInverseTrace = new HashMap<>();
48 private final Map<AnyPartialSymbol, Relation> inverseTrace = Collections.unmodifiableMap(mutableInverseTrace); 46 private final Map<AnyPartialSymbol, Relation> inverseTrace = Collections.unmodifiableMap(mutableInverseTrace);
47 private final Map<Rule, RuleDefinition> mutableInverseRuleDefinitionTrace = new LinkedHashMap<>();
49 48
50 @Override 49 @Override
51 public Problem getProblem() { 50 public Problem getProblem() {
@@ -129,6 +128,23 @@ class ProblemTraceImpl implements ProblemTrace {
129 return relation; 128 return relation;
130 } 129 }
131 130
131 void putRuleDefinition(RuleDefinition ruleDefinition, Rule rule) {
132 var oldRuleDefinition = mutableInverseRuleDefinitionTrace.put(rule, ruleDefinition);
133 if (oldRuleDefinition != null) {
134 throw new TracedException(oldRuleDefinition, "Rule definition %s was already mapped to rule"
135 .formatted(rule.getName()));
136 }
137 }
138
139 @Override
140 public RuleDefinition getRuleDefinition(Rule rule) {
141 var ruleDefinition = mutableInverseRuleDefinitionTrace.get(rule);
142 if (ruleDefinition == null) {
143 throw new IllegalArgumentException("No definition for rule: " + rule.getName());
144 }
145 return ruleDefinition;
146 }
147
132 @Override 148 @Override
133 public RuntimeException wrapException(TranslationException translationException) { 149 public RuntimeException wrapException(TranslationException translationException) {
134 var partialSymbol = translationException.getPartialSymbol(); 150 var partialSymbol = translationException.getPartialSymbol();
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java
index 4ec11bb9..c6485309 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java
@@ -8,6 +8,7 @@ package tools.refinery.language.web.semantics;
8import com.google.inject.Inject; 8import com.google.inject.Inject;
9import org.eclipse.emf.common.util.Diagnostic; 9import org.eclipse.emf.common.util.Diagnostic;
10import org.eclipse.emf.ecore.EObject; 10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.emf.ecore.EStructuralFeature;
11import org.eclipse.xtext.service.OperationCanceledManager; 12import org.eclipse.xtext.service.OperationCanceledManager;
12import org.eclipse.xtext.util.CancelIndicator; 13import org.eclipse.xtext.util.CancelIndicator;
13import org.eclipse.xtext.validation.CheckType; 14import org.eclipse.xtext.validation.CheckType;
@@ -18,11 +19,14 @@ import org.eclipse.xtext.web.server.validation.ValidationResult;
18import tools.refinery.generator.ModelSemantics; 19import tools.refinery.generator.ModelSemantics;
19import tools.refinery.generator.ModelSemanticsFactory; 20import tools.refinery.generator.ModelSemanticsFactory;
20import tools.refinery.language.model.problem.Problem; 21import tools.refinery.language.model.problem.Problem;
22import tools.refinery.language.model.problem.ProblemPackage;
21import tools.refinery.language.model.problem.ScopeDeclaration; 23import tools.refinery.language.model.problem.ScopeDeclaration;
24import tools.refinery.language.semantics.ProblemTrace;
22import tools.refinery.language.semantics.TracedException; 25import tools.refinery.language.semantics.TracedException;
23import tools.refinery.language.web.semantics.metadata.MetadataCreator; 26import tools.refinery.language.web.semantics.metadata.MetadataCreator;
24import tools.refinery.store.dse.propagation.PropagationRejectedResult; 27import tools.refinery.store.dse.propagation.PropagationRejectedResult;
25import tools.refinery.store.dse.propagation.PropagationResult; 28import tools.refinery.store.dse.propagation.PropagationResult;
29import tools.refinery.store.dse.transition.Rule;
26import tools.refinery.store.reasoning.literal.Concreteness; 30import tools.refinery.store.reasoning.literal.Concreteness;
27import tools.refinery.store.reasoning.scope.ScopePropagator; 31import tools.refinery.store.reasoning.scope.ScopePropagator;
28import tools.refinery.store.reasoning.translator.TranslationException; 32import tools.refinery.store.reasoning.translator.TranslationException;
@@ -79,18 +83,31 @@ class SemanticsWorker implements Callable<SemanticsResult> {
79 } 83 }
80 cancellationToken.checkCancelled(); 84 cancellationToken.checkCancelled();
81 var modelResult = createSemanticsModelResult(semantics); 85 var modelResult = createSemanticsModelResult(semantics);
82 return createSemanticsResult(modelResult, semantics.getPropagationResult()); 86 return createSemanticsResult(modelResult, semantics.getProblemTrace(), semantics.getPropagationResult());
83 } 87 }
84 88
85
86 private SemanticsResult getTracedErrorResult(EObject sourceElement, String message) { 89 private SemanticsResult getTracedErrorResult(EObject sourceElement, String message) {
90 var diagnostics = getTracedDiagnostics(sourceElement, null, message);
91 return getSemanticsResultWithDiagnostics(null, message, diagnostics);
92 }
93
94 private List<FeatureBasedDiagnostic> getTracedDiagnostics(
95 EObject sourceElement, EStructuralFeature feature, String message) {
87 if (sourceElement == null || !problem.eResource().equals(sourceElement.eResource())) { 96 if (sourceElement == null || !problem.eResource().equals(sourceElement.eResource())) {
88 return new SemanticsResult(message); 97 return List.of();
89 } 98 }
90 var diagnostic = new FeatureBasedDiagnostic(Diagnostic.ERROR, message, sourceElement, null, 0, 99 var diagnostic = new FeatureBasedDiagnostic(Diagnostic.ERROR, message, sourceElement, feature, 0,
91 CheckType.EXPENSIVE, DIAGNOSTIC_ID); 100 CheckType.EXPENSIVE, DIAGNOSTIC_ID);
92 var issues = convertIssues(List.of(diagnostic)); 101 return List.of(diagnostic);
93 return new SemanticsResult(issues); 102 }
103
104 private SemanticsResult getSemanticsResultWithDiagnostics(
105 SemanticsModelResult modelResult, String message, List<FeatureBasedDiagnostic> diagnostics) {
106 if (diagnostics.isEmpty()) {
107 return new SemanticsResult(modelResult, message);
108 }
109 var issues = convertIssues(diagnostics);
110 return new SemanticsResult(modelResult, issues);
94 } 111 }
95 112
96 private List<ValidationResult.Issue> convertIssues(List<FeatureBasedDiagnostic> diagnostics) { 113 private List<ValidationResult.Issue> convertIssues(List<FeatureBasedDiagnostic> diagnostics) {
@@ -114,26 +131,30 @@ class SemanticsWorker implements Callable<SemanticsResult> {
114 return new SemanticsModelResult(nodesMetadata, relationsMetadata, partialInterpretation); 131 return new SemanticsModelResult(nodesMetadata, relationsMetadata, partialInterpretation);
115 } 132 }
116 133
117 private SemanticsResult createSemanticsResult(SemanticsModelResult modelResult, 134 private SemanticsResult createSemanticsResult(
118 PropagationResult propagationResult) { 135 SemanticsModelResult modelResult, ProblemTrace trace, PropagationResult propagationResult) {
119 if (!(propagationResult instanceof PropagationRejectedResult rejectedResult)) { 136 if (!(propagationResult instanceof PropagationRejectedResult rejectedResult)) {
120 return new SemanticsResult(modelResult); 137 return new SemanticsResult(modelResult);
121 } 138 }
122 var message = rejectedResult.formatMessage(); 139 var message = rejectedResult.formatMessage();
123 if (!(rejectedResult.reason() instanceof ScopePropagator)) { 140 List<FeatureBasedDiagnostic> diagnostics = switch (rejectedResult.reason()) {
124 return new SemanticsResult(modelResult, message); 141 case ScopePropagator ignored -> getScopePropagatorDiagnostics(message);
125 } 142 case Rule rule -> getRuleDiagnostics(rule, trace, message);
126 var diagnostics = new ArrayList<FeatureBasedDiagnostic>(); 143 default -> List.of();
127 for (var statement : problem.getStatements()) { 144 };
128 if (statement instanceof ScopeDeclaration scopeDeclaration) { 145 return getSemanticsResultWithDiagnostics(modelResult, message, diagnostics);
129 diagnostics.add(new FeatureBasedDiagnostic(Diagnostic.ERROR, message, scopeDeclaration, null, 0, 146 }
130 CheckType.EXPENSIVE, DIAGNOSTIC_ID)); 147
131 } 148 private List<FeatureBasedDiagnostic> getScopePropagatorDiagnostics(String message) {
132 } 149 return problem.getStatements().stream()
133 if (diagnostics.isEmpty()) { 150 .filter(ScopeDeclaration.class::isInstance)
134 return new SemanticsResult(modelResult, message); 151 .map(eObject -> new FeatureBasedDiagnostic(Diagnostic.ERROR, message, eObject, null, 0,
135 } 152 CheckType.EXPENSIVE, DIAGNOSTIC_ID))
136 var issues = convertIssues(diagnostics); 153 .toList();
137 return new SemanticsResult(modelResult, issues); 154 }
155
156 private List<FeatureBasedDiagnostic> getRuleDiagnostics(Rule rule, ProblemTrace trace, String message) {
157 var ruleDefinition = trace.getRuleDefinition(rule);
158 return getTracedDiagnostics(ruleDefinition, ProblemPackage.Literals.NAMED_ELEMENT__NAME, message);
138 } 159 }
139} 160}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java
index f8a89b30..2a22f00d 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationBuilder.java
@@ -27,6 +27,8 @@ public interface PropagationBuilder extends ModelAdapterBuilder {
27 27
28 PropagationBuilder propagator(Propagator propagator); 28 PropagationBuilder propagator(Propagator propagator);
29 29
30 PropagationBuilder throwOnFatalRejection(boolean throwOnFatalRejection);
31
30 @Override 32 @Override
31 PropagationStoreAdapter build(ModelStore store); 33 PropagationStoreAdapter build(ModelStore store);
32} 34}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationRejectedResult.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationRejectedResult.java
index 6d696de7..0ef1dc38 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationRejectedResult.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/PropagationRejectedResult.java
@@ -5,7 +5,11 @@
5 */ 5 */
6package tools.refinery.store.dse.propagation; 6package tools.refinery.store.dse.propagation;
7 7
8public record PropagationRejectedResult(Object reason, String message) implements PropagationResult { 8public record PropagationRejectedResult(Object reason, String message, boolean fatal) implements PropagationResult {
9 public PropagationRejectedResult(Object reason, String message) {
10 this(reason, message, false);
11 }
12
9 @Override 13 @Override
10 public PropagationResult andThen(PropagationResult next) { 14 public PropagationResult andThen(PropagationResult next) {
11 return this; 15 return this;
@@ -18,7 +22,7 @@ public record PropagationRejectedResult(Object reason, String message) implement
18 22
19 @Override 23 @Override
20 public void throwIfRejected() { 24 public void throwIfRejected() {
21 throw new IllegalArgumentException(formatMessage()); 25 throw new IllegalStateException(formatMessage());
22 } 26 }
23 27
24 public String formatMessage() { 28 public String formatMessage() {
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java
index fdd19217..7158a7ab 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationAdapterImpl.java
@@ -5,10 +5,7 @@
5 */ 5 */
6package tools.refinery.store.dse.propagation.impl; 6package tools.refinery.store.dse.propagation.impl;
7 7
8import tools.refinery.store.dse.propagation.BoundPropagator; 8import tools.refinery.store.dse.propagation.*;
9import tools.refinery.store.dse.propagation.PropagationAdapter;
10import tools.refinery.store.dse.propagation.PropagationResult;
11import tools.refinery.store.dse.propagation.PropagationStoreAdapter;
12import tools.refinery.store.model.Model; 9import tools.refinery.store.model.Model;
13 10
14class PropagationAdapterImpl implements PropagationAdapter { 11class PropagationAdapterImpl implements PropagationAdapter {
@@ -35,6 +32,11 @@ class PropagationAdapterImpl implements PropagationAdapter {
35 lastResult = propagateOne(); 32 lastResult = propagateOne();
36 result = result.andThen(lastResult); 33 result = result.andThen(lastResult);
37 } while (lastResult.isChanged()); 34 } while (lastResult.isChanged());
35 if (lastResult instanceof PropagationRejectedResult rejectedResult &&
36 rejectedResult.fatal() &&
37 storeAdapter.isThrowOnFatalRejection()) {
38 rejectedResult.throwIfRejected();
39 }
38 return result; 40 return result;
39 } 41 }
40 42
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java
index c844a89f..da32fe44 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationBuilderImpl.java
@@ -20,6 +20,7 @@ public class PropagationBuilderImpl extends AbstractModelAdapterBuilder<Propagat
20 implements PropagationBuilder { 20 implements PropagationBuilder {
21 private final Set<Rule> propagationRules = new LinkedHashSet<>(); 21 private final Set<Rule> propagationRules = new LinkedHashSet<>();
22 private final Deque<Propagator> propagators = new ArrayDeque<>(); 22 private final Deque<Propagator> propagators = new ArrayDeque<>();
23 private boolean throwOnFatalRejection = true;
23 24
24 @Override 25 @Override
25 public PropagationBuilder rule(Rule propagationRule) { 26 public PropagationBuilder rule(Rule propagationRule) {
@@ -36,6 +37,12 @@ public class PropagationBuilderImpl extends AbstractModelAdapterBuilder<Propagat
36 } 37 }
37 38
38 @Override 39 @Override
40 public PropagationBuilder throwOnFatalRejection(boolean throwOnFatalRejection) {
41 this.throwOnFatalRejection = throwOnFatalRejection;
42 return this;
43 }
44
45 @Override
39 protected void doConfigure(ModelStoreBuilder storeBuilder) { 46 protected void doConfigure(ModelStoreBuilder storeBuilder) {
40 super.doConfigure(storeBuilder); 47 super.doConfigure(storeBuilder);
41 if (!propagationRules.isEmpty()) { 48 if (!propagationRules.isEmpty()) {
@@ -48,6 +55,6 @@ public class PropagationBuilderImpl extends AbstractModelAdapterBuilder<Propagat
48 55
49 @Override 56 @Override
50 protected PropagationStoreAdapter doBuild(ModelStore store) { 57 protected PropagationStoreAdapter doBuild(ModelStore store) {
51 return new PropagationStoreAdapterImpl(store, List.copyOf(propagators)); 58 return new PropagationStoreAdapterImpl(store, List.copyOf(propagators), throwOnFatalRejection);
52 } 59 }
53} 60}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java
index a223caed..4f7b8622 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/PropagationStoreAdapterImpl.java
@@ -13,13 +13,17 @@ import tools.refinery.store.model.ModelStore;
13 13
14import java.util.List; 14import java.util.List;
15 15
16// This should not be a record, because we don't want auto-generated {@code equals} and {@code hashCode} methods.
17@SuppressWarnings("ClassCanBeRecord")
16class PropagationStoreAdapterImpl implements PropagationStoreAdapter { 18class PropagationStoreAdapterImpl implements PropagationStoreAdapter {
17 private final ModelStore store; 19 private final ModelStore store;
18 private final List<Propagator> propagators; 20 private final List<Propagator> propagators;
21 private final boolean throwOnFatalRejection;
19 22
20 PropagationStoreAdapterImpl(ModelStore store, List<Propagator> propagators) { 23 PropagationStoreAdapterImpl(ModelStore store, List<Propagator> propagators, boolean throwOnFatalRejection) {
21 this.store = store; 24 this.store = store;
22 this.propagators = propagators; 25 this.propagators = propagators;
26 this.throwOnFatalRejection = throwOnFatalRejection;
23 } 27 }
24 28
25 @Override 29 @Override
@@ -35,4 +39,8 @@ class PropagationStoreAdapterImpl implements PropagationStoreAdapter {
35 List<Propagator> getPropagators() { 39 List<Propagator> getPropagators() {
36 return propagators; 40 return propagators;
37 } 41 }
42
43 boolean isThrowOnFatalRejection() {
44 return throwOnFatalRejection;
45 }
38} 46}
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java
index f91882be..cc1e0486 100644
--- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/propagation/impl/rule/BoundPropagationRule.java
@@ -44,7 +44,8 @@ class BoundPropagationRule {
44 44
45 public PropagationResult fireAll() { 45 public PropagationResult fireAll() {
46 if (!firedActivations.isEmpty()) { 46 if (!firedActivations.isEmpty()) {
47 throw new IllegalStateException("Stuck propagation rule '%s'.".formatted(rule.getName())); 47 return new PropagationRejectedResult(rule, "Propagation rule '%s' got stuck.".formatted(rule.getName()),
48 true);
48 } 49 }
49 if (resultSet.size() == 0) { 50 if (resultSet.size() == 0) {
50 return PropagationResult.UNCHANGED; 51 return PropagationResult.UNCHANGED;