aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/main/java
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-01-23 20:27:55 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-01-29 00:02:28 +0100
commitd91acf3690682d243dbc150df902525b6e545c2f (patch)
treef39695cc193828df0b78030b5a56bd968e277457 /subprojects/store-query-viatra/src/main/java
parentchore(deps): bump dependencies (diff)
downloadrefinery-d91acf3690682d243dbc150df902525b6e545c2f.tar.gz
refinery-d91acf3690682d243dbc150df902525b6e545c2f.tar.zst
refinery-d91acf3690682d243dbc150df902525b6e545c2f.zip
refactor: Model store and query API
Use Adapters to simplify API usage.
Diffstat (limited to 'subprojects/store-query-viatra/src/main/java')
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQuery.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java48
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java8
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java140
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java117
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java116
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java58
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraQueryableModel.java222
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java8
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java14
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java34
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SimplePQuery.java)7
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RawPatternMatcher.java)13
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java4
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java46
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java66
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java40
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java35
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java24
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java112
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdate.java32
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateBuffer.java47
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateTranslator.java73
26 files changed, 638 insertions, 674 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQuery.java
new file mode 100644
index 00000000..ecac570b
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQuery.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.query.viatra;
2
3import tools.refinery.store.adapter.ModelAdapterBuilderFactory;
4import tools.refinery.store.model.ModelStoreBuilder;
5import tools.refinery.store.query.ModelQuery;
6import tools.refinery.store.query.ModelQueryAdapter;
7import tools.refinery.store.query.viatra.internal.ViatraModelQueryBuilderImpl;
8
9public final class ViatraModelQuery extends ModelAdapterBuilderFactory<ModelQueryAdapter, ViatraModelQueryStoreAdapter,
10 ViatraModelQueryBuilder> {
11 public static final ViatraModelQuery ADAPTER = new ViatraModelQuery();
12
13 private ViatraModelQuery() {
14 super(ModelQueryAdapter.class, ViatraModelQueryStoreAdapter.class, ViatraModelQueryBuilder.class);
15 extendsAdapter(ModelQuery.ADAPTER);
16 }
17
18 @Override
19 public ViatraModelQueryBuilder createBuilder(ModelStoreBuilder storeBuilder) {
20 return new ViatraModelQueryBuilderImpl(storeBuilder);
21 }
22}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
new file mode 100644
index 00000000..ee445a79
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
4import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
5import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
6import tools.refinery.store.model.ModelStore;
7import tools.refinery.store.query.DNF;
8import tools.refinery.store.query.ModelQueryBuilder;
9
10import java.util.Collection;
11import java.util.function.Function;
12
13@SuppressWarnings("UnusedReturnValue")
14public interface ViatraModelQueryBuilder extends ModelQueryBuilder {
15 ViatraModelQueryBuilder engineOptions(ViatraQueryEngineOptions engineOptions);
16
17 ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint);
18
19 ViatraModelQueryBuilder backend(IQueryBackendFactory queryBackendFactory);
20
21 ViatraModelQueryBuilder cachingBackend(IQueryBackendFactory queryBackendFactory);
22
23 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory);
24
25 @Override
26 default ViatraModelQueryBuilder queries(DNF... queries) {
27 ModelQueryBuilder.super.queries(queries);
28 return this;
29 }
30
31 @Override
32 default ViatraModelQueryBuilder queries(Collection<? extends DNF> queries) {
33 ModelQueryBuilder.super.queries(queries);
34 return this;
35 }
36
37 @Override
38 ViatraModelQueryBuilder query(DNF query);
39
40 ViatraModelQueryBuilder query(DNF query, QueryEvaluationHint queryEvaluationHint);
41
42 ViatraModelQueryBuilder computeHint(Function<DNF, QueryEvaluationHint> computeHint);
43
44 ViatraModelQueryBuilder hint(DNF dnf, QueryEvaluationHint queryEvaluationHint);
45
46 @Override
47 ViatraModelQueryStoreAdapter createStoreAdapter(ModelStore store);
48}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java
new file mode 100644
index 00000000..d52575d2
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java
@@ -0,0 +1,8 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
4import tools.refinery.store.query.ModelQueryStoreAdapter;
5
6public interface ViatraModelQueryStoreAdapter extends ModelQueryStoreAdapter {
7 ViatraQueryEngineOptions getEngineOptions();
8}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java
deleted file mode 100644
index 94d2db4f..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java
+++ /dev/null
@@ -1,140 +0,0 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
4import tools.refinery.store.model.ModelDiffCursor;
5import tools.refinery.store.model.ModelStore;
6import tools.refinery.store.model.ModelStoreImpl;
7import tools.refinery.store.model.RelationLike;
8import tools.refinery.store.model.representation.AnyDataRepresentation;
9import tools.refinery.store.model.representation.DataRepresentation;
10import tools.refinery.store.query.DNF;
11import tools.refinery.store.query.DNFAnd;
12import tools.refinery.store.query.QueryableModel;
13import tools.refinery.store.query.QueryableModelStore;
14import tools.refinery.store.query.atom.*;
15import tools.refinery.store.query.viatra.internal.RawPatternMatcher;
16import tools.refinery.store.query.viatra.internal.ViatraQueryableModel;
17import tools.refinery.store.query.viatra.internal.pquery.DNF2PQuery;
18import tools.refinery.store.query.view.AnyRelationView;
19
20import java.util.Collections;
21import java.util.HashMap;
22import java.util.Map;
23import java.util.Set;
24
25public class ViatraQueryableModelStore implements QueryableModelStore {
26 protected final ModelStore store;
27
28 protected final Set<AnyRelationView> relationViews;
29
30 protected final Map<DNF, GenericQuerySpecification<RawPatternMatcher>> predicates;
31
32 public ViatraQueryableModelStore(ModelStore store, Set<AnyRelationView> relationViews,
33 Set<DNF> predicates) {
34 this.store = store;
35 validateViews(store.getDataRepresentations(), relationViews);
36 this.relationViews = Collections.unmodifiableSet(relationViews);
37 validatePredicates(relationViews, predicates);
38 this.predicates = initPredicates(predicates);
39 }
40
41 public ViatraQueryableModelStore(Set<AnyDataRepresentation> dataRepresentations,
42 Set<AnyRelationView> relationViews, Set<DNF> predicates) {
43 this(new ModelStoreImpl(dataRepresentations), relationViews, predicates);
44 }
45
46 private void validateViews(Set<AnyDataRepresentation> dataRepresentations, Set<AnyRelationView> relationViews) {
47 for (var relationView : relationViews) {
48 if (!dataRepresentations.contains(relationView.getRepresentation())) {
49 throw new IllegalArgumentException("%s %s added to %s without a referred representation.".formatted(
50 DataRepresentation.class.getSimpleName(), relationView.getName(),
51 QueryableModelStore.class.getSimpleName()));
52 }
53 }
54 }
55
56 private void validatePredicates(Set<AnyRelationView> relationViews, Set<DNF> predicates) {
57 for (DNF dnfPredicate : predicates) {
58 for (DNFAnd clause : dnfPredicate.getClauses()) {
59 for (DNFAtom atom : clause.constraints()) {
60 if (atom instanceof RelationViewAtom relationViewAtom) {
61 validateRelationAtom(relationViews, dnfPredicate, relationViewAtom);
62 } else if (atom instanceof CallAtom<?> queryCallAtom) {
63 validatePredicateAtom(predicates, dnfPredicate, queryCallAtom);
64 } else if (!(atom instanceof EquivalenceAtom || atom instanceof ConstantAtom)) {
65 throw new IllegalArgumentException("Unknown constraint: " + atom.toString());
66 }
67 }
68 }
69 }
70 }
71
72 private void validateRelationAtom(Set<AnyRelationView> relationViews, DNF dnfPredicate,
73 RelationViewAtom relationViewAtom) {
74 if (!relationViews.contains(relationViewAtom.getTarget())) {
75 throw new IllegalArgumentException(
76 "%s %s contains reference to a view %s that is not in the model.".formatted(
77 DNF.class.getSimpleName(), dnfPredicate.getUniqueName(),
78 relationViewAtom.getTarget().getName()));
79 }
80 }
81
82 private void validatePredicateReference(Set<DNF> predicates, DNF dnfPredicate, RelationLike target) {
83 if (!(target instanceof DNF dnfTarget) || !predicates.contains(dnfTarget)) {
84 throw new IllegalArgumentException(
85 "%s %s contains reference to a predicate %s that is not in the model.".formatted(
86 DNF.class.getSimpleName(), dnfPredicate.getUniqueName(), target.getName()));
87 }
88 }
89
90 private void validatePredicateAtom(Set<DNF> predicates, DNF dnfPredicate, CallAtom<?> queryCallAtom) {
91 validatePredicateReference(predicates, dnfPredicate, queryCallAtom.getTarget());
92 }
93
94
95 private Map<DNF, GenericQuerySpecification<RawPatternMatcher>> initPredicates(Set<DNF> predicates) {
96 Map<DNF, GenericQuerySpecification<RawPatternMatcher>> result = new HashMap<>();
97 var dnf2PQuery = new DNF2PQuery();
98 for (DNF dnfPredicate : predicates) {
99 GenericQuerySpecification<RawPatternMatcher> query = dnf2PQuery.translate(dnfPredicate).build();
100 result.put(dnfPredicate, query);
101 }
102
103 return result;
104 }
105
106 @Override
107 public Set<AnyDataRepresentation> getDataRepresentations() {
108 return store.getDataRepresentations();
109 }
110
111 @Override
112 public Set<AnyRelationView> getViews() {
113 return this.relationViews;
114 }
115
116 @Override
117 public Set<DNF> getPredicates() {
118 return predicates.keySet();
119 }
120
121 @Override
122 public QueryableModel createModel() {
123 return new ViatraQueryableModel(this, this.store.createModel(), predicates);
124 }
125
126 @Override
127 public QueryableModel createModel(long state) {
128 return new ViatraQueryableModel(this, this.store.createModel(state), predicates);
129 }
130
131 @Override
132 public synchronized Set<Long> getStates() {
133 return this.store.getStates();
134 }
135
136 @Override
137 public synchronized ModelDiffCursor getDiffCursor(long from, long to) {
138 return this.store.getDiffCursor(from, to);
139 }
140}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
index 133c4c72..21dcaf15 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
@@ -6,34 +6,20 @@ import org.eclipse.viatra.query.runtime.api.scope.IEngineContext;
6import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; 6import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener;
7import org.eclipse.viatra.query.runtime.api.scope.QueryScope; 7import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
8import tools.refinery.store.model.Model; 8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.representation.Relation;
10import tools.refinery.store.query.viatra.internal.context.RelationalEngineContext; 9import tools.refinery.store.query.viatra.internal.context.RelationalEngineContext;
11import tools.refinery.store.query.viatra.internal.viewupdate.ModelUpdateListener; 10import tools.refinery.store.query.viatra.internal.update.ModelUpdateListener;
12import tools.refinery.store.query.view.AnyRelationView; 11import tools.refinery.store.query.view.AnyRelationView;
13import tools.refinery.store.tuple.Tuple;
14 12
15import java.util.Set; 13import java.util.Collection;
16 14
17public class RelationalScope extends QueryScope { 15public class RelationalScope extends QueryScope {
18 private final Model model; 16 private final Model model;
19 17
20 private final ModelUpdateListener updateListener; 18 private final ModelUpdateListener updateListener;
21 19
22 public RelationalScope(Model model, Set<AnyRelationView> relationViews) { 20 public RelationalScope(Model model, Collection<AnyRelationView> relationViews) {
23 this.model = model; 21 this.model = model;
24 this.updateListener = new ModelUpdateListener(relationViews); 22 updateListener = new ModelUpdateListener(model, relationViews);
25 }
26
27 public <D> void processUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
28 updateListener.addUpdate(relation, key, oldValue, newValue);
29 }
30
31 public boolean hasChanges() {
32 return updateListener.hasChanges();
33 }
34
35 public void flush() {
36 updateListener.flush();
37 } 23 }
38 24
39 @Override 25 @Override
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
new file mode 100644
index 00000000..3c276935
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
@@ -0,0 +1,117 @@
1package tools.refinery.store.query.viatra.internal;
2
3import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine;
4import org.eclipse.viatra.query.runtime.api.GenericQueryGroup;
5import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
6import org.eclipse.viatra.query.runtime.internal.apiimpl.ViatraQueryEngineImpl;
7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend;
8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.DNF;
11import tools.refinery.store.query.ModelQueryAdapter;
12import tools.refinery.store.query.ModelQueryStoreAdapter;
13import tools.refinery.store.query.ResultSet;
14
15import java.lang.invoke.MethodHandle;
16import java.lang.invoke.MethodHandles;
17import java.util.*;
18
19public class ViatraModelQueryAdapterImpl implements ModelQueryAdapter {
20 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery";
21 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends";
22
23 private final Model model;
24 private final ViatraModelQueryStoreAdapterImpl storeAdapter;
25 private final ViatraQueryEngineImpl queryEngine;
26 private final MethodHandle setUpdatePropagationDelayedHandle;
27 private final MethodHandle getQueryBackendsHandle;
28 private final Map<DNF, ResultSet> resultSets;
29
30 ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) {
31 this.model = model;
32 this.storeAdapter = storeAdapter;
33 var scope = new RelationalScope(model, storeAdapter.getRelationViews());
34 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope);
35
36 try {
37 var lookup = MethodHandles.privateLookupIn(ViatraQueryEngineImpl.class, MethodHandles.lookup());
38 setUpdatePropagationDelayedHandle = lookup.findSetter(ViatraQueryEngineImpl.class,
39 DELAY_MESSAGE_DELIVERY_FIELD_NAME, Boolean.TYPE);
40 getQueryBackendsHandle = lookup.findGetter(ViatraQueryEngineImpl.class, QUERY_BACKENDS_FIELD_NAME,
41 Map.class);
42 } catch (IllegalAccessException | NoSuchFieldException e) {
43 throw new IllegalStateException("Cannot access private members of %s"
44 .formatted(ViatraQueryEngineImpl.class.getName()), e);
45 }
46
47 var querySpecifications = storeAdapter.getQuerySpecifications();
48 GenericQueryGroup.of(
49 Collections.<IQuerySpecification<?>>unmodifiableCollection(querySpecifications.values()).stream()
50 ).prepare(queryEngine);
51 resultSets = new HashMap<>(querySpecifications.size());
52 for (var entry : querySpecifications.entrySet()) {
53 var matcher = queryEngine.getMatcher(entry.getValue());
54 resultSets.put(entry.getKey(), matcher);
55 }
56
57 setUpdatePropagationDelayed(true);
58 }
59
60 private void setUpdatePropagationDelayed(boolean value) {
61 try {
62 setUpdatePropagationDelayedHandle.invokeExact(queryEngine, value);
63 } catch (Error e) {
64 // Fatal JVM errors should not be wrapped.
65 throw e;
66 } catch (Throwable e) {
67 throw new IllegalStateException("Cannot set %s".formatted(DELAY_MESSAGE_DELIVERY_FIELD_NAME), e);
68 }
69 }
70
71 private Collection<IQueryBackend> getQueryBackends() {
72 try {
73 @SuppressWarnings("unchecked")
74 var backendMap = (Map<IQueryBackendFactory, IQueryBackend>) getQueryBackendsHandle.invokeExact(queryEngine);
75 return backendMap.values();
76 } catch (Error e) {
77 // Fatal JVM errors should not be wrapped.
78 throw e;
79 } catch (Throwable e) {
80 throw new IllegalStateException("Cannot get %s".formatted(QUERY_BACKENDS_FIELD_NAME), e);
81 }
82 }
83
84 @Override
85 public Model getModel() {
86 return model;
87 }
88
89 @Override
90 public ModelQueryStoreAdapter getStoreAdapter() {
91 return storeAdapter;
92 }
93
94 @Override
95 public ResultSet getResultSet(DNF query) {
96 var resultSet = resultSets.get(query);
97 if (resultSet == null) {
98 throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name()));
99 }
100 return resultSet;
101 }
102
103 @Override
104 public void flushChanges() {
105 if (!queryEngine.isUpdatePropagationDelayed()) {
106 throw new IllegalStateException("Trying to flush changes while changes are already being flushed");
107 }
108 setUpdatePropagationDelayed(false);
109 try {
110 for (var queryBackend : getQueryBackends()) {
111 queryBackend.flushUpdates();
112 }
113 } finally {
114 setUpdatePropagationDelayed(true);
115 }
116 }
117}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
new file mode 100644
index 00000000..5105c9a7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
@@ -0,0 +1,116 @@
1package tools.refinery.store.query.viatra.internal;
2
3import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory;
6import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
7import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
8import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory;
9import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.model.ModelStoreBuilder;
12import tools.refinery.store.query.DNF;
13import tools.refinery.store.query.viatra.ViatraModelQueryBuilder;
14import tools.refinery.store.query.viatra.internal.pquery.DNF2PQuery;
15import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher;
16
17import java.util.Collections;
18import java.util.LinkedHashMap;
19import java.util.Map;
20import java.util.function.Function;
21
22public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder implements ViatraModelQueryBuilder {
23 private ViatraQueryEngineOptions.Builder engineOptionsBuilder;
24 private final DNF2PQuery dnf2PQuery = new DNF2PQuery();
25 private final Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>();
26
27 public ViatraModelQueryBuilderImpl(ModelStoreBuilder storeBuilder) {
28 super(storeBuilder);
29 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder()
30 .withDefaultBackend(ReteBackendFactory.INSTANCE)
31 .withDefaultCachingBackend(ReteBackendFactory.INSTANCE)
32 .withDefaultSearchBackend(LocalSearchGenericBackendFactory.INSTANCE);
33 }
34
35 @Override
36 public ViatraModelQueryBuilder engineOptions(ViatraQueryEngineOptions engineOptions) {
37 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder(engineOptions);
38 return this;
39 }
40
41 @Override
42 public ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint) {
43 engineOptionsBuilder.withDefaultHint(queryEvaluationHint);
44 return this;
45 }
46
47 @Override
48 public ViatraModelQueryBuilder backend(IQueryBackendFactory queryBackendFactory) {
49 engineOptionsBuilder.withDefaultBackend(queryBackendFactory);
50 return this;
51 }
52
53 @Override
54 public ViatraModelQueryBuilder cachingBackend(IQueryBackendFactory queryBackendFactory) {
55 engineOptionsBuilder.withDefaultCachingBackend(queryBackendFactory);
56 return this;
57 }
58
59 @Override
60 public ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory) {
61 engineOptionsBuilder.withDefaultSearchBackend(queryBackendFactory);
62 return this;
63 }
64
65 @Override
66 public ViatraModelQueryBuilder query(DNF query) {
67 if (querySpecifications.containsKey(query)) {
68 throw new IllegalArgumentException("%s was already added to the query engine".formatted(query.name()));
69 }
70 var pQuery = dnf2PQuery.translate(query);
71 querySpecifications.put(query, pQuery.build());
72 return this;
73 }
74
75 @Override
76 public ViatraModelQueryBuilder query(DNF query, QueryEvaluationHint queryEvaluationHint) {
77 query(query);
78 hint(query, queryEvaluationHint);
79 return this;
80 }
81
82 @Override
83 public ViatraModelQueryBuilder computeHint(Function<DNF, QueryEvaluationHint> computeHint) {
84 dnf2PQuery.setComputeHint(computeHint);
85 return this;
86 }
87
88 @Override
89 public ViatraModelQueryBuilder hint(DNF dnf, QueryEvaluationHint queryEvaluationHint) {
90 var pQuery = dnf2PQuery.getAlreadyTranslated(dnf);
91 if (pQuery == null) {
92 throw new IllegalArgumentException(
93 "Cannot specify hint for %s, because it was not added to the query engine".formatted(dnf.name()));
94 }
95 pQuery.setEvaluationHints(queryEvaluationHint);
96 return this;
97 }
98
99 @Override
100 public ViatraModelQueryStoreAdapterImpl createStoreAdapter(ModelStore store) {
101 validateSymbols(store);
102 return new ViatraModelQueryStoreAdapterImpl(store, engineOptionsBuilder.build(), dnf2PQuery.getRelationViews(),
103 Collections.unmodifiableMap(querySpecifications));
104 }
105
106 private void validateSymbols(ModelStore store) {
107 var symbols = store.getSymbols();
108 for (var relationView : dnf2PQuery.getRelationViews()) {
109 var symbol = relationView.getSymbol();
110 if (!symbols.contains(symbol)) {
111 throw new IllegalArgumentException("Cannot query relation view %s: symbol %s is not in the model"
112 .formatted(relationView, symbol));
113 }
114 }
115 }
116}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
new file mode 100644
index 00000000..d77b7f4b
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
@@ -0,0 +1,58 @@
1package tools.refinery.store.query.viatra.internal;
2
3import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
5import tools.refinery.store.model.Model;
6import tools.refinery.store.model.ModelStore;
7import tools.refinery.store.query.DNF;
8import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter;
9import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher;
10import tools.refinery.store.query.view.AnyRelationView;
11
12import java.util.Collection;
13import java.util.Map;
14
15public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAdapter {
16 private final ModelStore store;
17 private final ViatraQueryEngineOptions engineOptions;
18 private final Collection<AnyRelationView> relationViews;
19 private final Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications;
20
21 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions,
22 Collection<AnyRelationView> relationViews,
23 Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications) {
24 this.store = store;
25 this.engineOptions = engineOptions;
26 this.relationViews = relationViews;
27 this.querySpecifications = querySpecifications;
28 }
29
30 @Override
31 public ModelStore getStore() {
32 return store;
33 }
34
35 @Override
36 public Collection<AnyRelationView> getRelationViews() {
37 return relationViews;
38 }
39
40 @Override
41 public Collection<DNF> getQueries() {
42 return querySpecifications.keySet();
43 }
44
45 Map<DNF, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() {
46 return querySpecifications;
47 }
48
49 @Override
50 public ViatraQueryEngineOptions getEngineOptions() {
51 return engineOptions;
52 }
53
54 @Override
55 public ViatraModelQueryAdapterImpl createModelAdapter(Model model) {
56 return new ViatraModelQueryAdapterImpl(model, this);
57 }
58}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraQueryableModel.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraQueryableModel.java
deleted file mode 100644
index 5b06e266..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraQueryableModel.java
+++ /dev/null
@@ -1,222 +0,0 @@
1package tools.refinery.store.query.viatra.internal;
2
3import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine;
4import org.eclipse.viatra.query.runtime.api.GenericQueryGroup;
5import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
6import org.eclipse.viatra.query.runtime.api.IQueryGroup;
7import tools.refinery.store.map.Cursor;
8import tools.refinery.store.map.DiffCursor;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.model.ModelDiffCursor;
11import tools.refinery.store.model.representation.AnyDataRepresentation;
12import tools.refinery.store.model.representation.DataRepresentation;
13import tools.refinery.store.model.representation.Relation;
14import tools.refinery.store.query.QueryableModel;
15import tools.refinery.store.query.QueryableModelStore;
16import tools.refinery.store.query.DNF;
17import tools.refinery.store.tuple.Tuple;
18import tools.refinery.store.tuple.TupleLike;
19
20import java.util.HashMap;
21import java.util.Map;
22import java.util.Optional;
23import java.util.Set;
24import java.util.stream.Stream;
25
26public class ViatraQueryableModel implements QueryableModel {
27 protected final QueryableModelStore store;
28
29 protected final Model model;
30
31 protected final Map<DNF, GenericQuerySpecification<RawPatternMatcher>> predicates2PQuery;
32
33 protected RelationalScope scope;
34
35 protected AdvancedViatraQueryEngine engine;
36
37 protected Map<DNF, RawPatternMatcher> predicate2Matcher;
38
39 public ViatraQueryableModel(QueryableModelStore store, Model model,
40 Map<DNF, GenericQuerySpecification<RawPatternMatcher>> predicates2PQuery) {
41 this.store = store;
42 this.model = model;
43 this.predicates2PQuery = predicates2PQuery;
44 initEngine();
45 }
46
47 private void initEngine() {
48 this.scope = new RelationalScope(this.model, this.store.getViews());
49 this.engine = AdvancedViatraQueryEngine.createUnmanagedEngine(this.scope);
50 this.predicate2Matcher = initMatchers(this.engine, this.predicates2PQuery);
51 }
52
53 private Map<DNF, RawPatternMatcher> initMatchers(
54 AdvancedViatraQueryEngine engine,
55 Map<DNF, GenericQuerySpecification<RawPatternMatcher>> predicates2pQuery) {
56 // 1. prepare group
57 IQueryGroup queryGroup = GenericQueryGroup.of(Set.copyOf(predicates2pQuery.values()));
58 engine.prepareGroup(queryGroup, null);
59
60 // 2. then get all matchers
61 Map<DNF, RawPatternMatcher> result = new HashMap<>();
62 for (var entry : predicates2pQuery.entrySet()) {
63 var matcher = engine.getMatcher(entry.getValue());
64 result.put(entry.getKey(), matcher);
65 }
66 return result;
67 }
68
69 @Override
70 public Set<AnyDataRepresentation> getDataRepresentations() {
71 return model.getDataRepresentations();
72 }
73
74 @Override
75 public Set<DNF> getPredicates() {
76 return store.getPredicates();
77 }
78
79 @Override
80 public <K, V> V get(DataRepresentation<K, V> representation, K key) {
81 return model.get(representation, key);
82 }
83
84 @Override
85 public <K, V> Cursor<K, V> getAll(DataRepresentation<K, V> representation) {
86 return model.getAll(representation);
87 }
88
89 @SuppressWarnings("unchecked")
90 @Override
91 public <K, V> V put(DataRepresentation<K, V> representation, K key, V value) {
92 V oldValue = this.model.put(representation, key, value);
93 if (representation instanceof Relation<?> relation) {
94 this.scope.processUpdate((Relation<V>) relation, (Tuple) key, oldValue, value);
95 }
96 return oldValue;
97 }
98
99 @Override
100 public <K, V> void putAll(DataRepresentation<K, V> representation, Cursor<K, V> cursor) {
101 if (representation instanceof Relation<?>) {
102 //noinspection RedundantSuppression
103 @SuppressWarnings("unchecked")
104 Relation<V> relation = (Relation<V>) representation;
105 while (cursor.move()) {
106 Tuple key = (Tuple) cursor.getKey();
107 V newValue = cursor.getValue();
108 V oldValue = this.model.put(relation, key, newValue);
109 this.scope.processUpdate(relation, key, oldValue, newValue);
110 }
111 } else {
112 this.model.putAll(representation, cursor);
113 }
114 }
115
116 @Override
117 public long getSize(AnyDataRepresentation representation) {
118 return model.getSize(representation);
119 }
120
121 protected RawPatternMatcher getMatcher(DNF predicate) {
122 var result = this.predicate2Matcher.get(predicate);
123 if (result == null) {
124 throw new IllegalArgumentException("Model does not contain predicate %s".formatted(predicate.getName()));
125 } else
126 return result;
127 }
128
129 protected void validateParameters(DNF predicate, Tuple parameters) {
130 int predicateArity = predicate.getParameters().size();
131 int parameterArity = parameters.getSize();
132 if (parameterArity != predicateArity) {
133 throw new IllegalArgumentException(
134 "Predicate %s with %d arity called with different number of parameters (%d)"
135 .formatted(predicate.getName(), predicateArity, parameterArity));
136 }
137 }
138
139 @Override
140 public boolean hasResult(DNF predicate) {
141 return getMatcher(predicate).hasResult();
142 }
143
144 @Override
145 public boolean hasResult(DNF predicate, Tuple parameters) {
146 validateParameters(predicate, parameters);
147 return getMatcher(predicate).hasResult(parameters);
148 }
149
150 @Override
151 public Optional<TupleLike> oneResult(DNF predicate) {
152 return getMatcher(predicate).oneResult();
153 }
154
155 @Override
156 public Optional<TupleLike> oneResult(DNF predicate, Tuple parameters) {
157 validateParameters(predicate, parameters);
158 return getMatcher(predicate).oneResult(parameters);
159 }
160
161 @Override
162 public Stream<TupleLike> allResults(DNF predicate) {
163 return getMatcher(predicate).allResults();
164 }
165
166 @Override
167 public Stream<TupleLike> allResults(DNF predicate, Tuple parameters) {
168 validateParameters(predicate, parameters);
169 return getMatcher(predicate).allResults(parameters);
170 }
171
172 @Override
173 public int countResults(DNF predicate) {
174 return getMatcher(predicate).countResults();
175 }
176
177 @Override
178 public int countResults(DNF predicate, Tuple parameters) {
179 validateParameters(predicate, parameters);
180 return getMatcher(predicate).countResults(parameters);
181
182 }
183
184 @Override
185 public boolean hasChanges() {
186 return scope.hasChanges();
187 }
188
189 @Override
190 public void flushChanges() {
191 this.scope.flush();
192 }
193
194 @Override
195 public ModelDiffCursor getDiffCursor(long to) {
196 return model.getDiffCursor(to);
197 }
198
199 @Override
200 public long commit() {
201 return this.model.commit();
202 }
203
204 @Override
205 public void restore(long state) {
206 restoreWithDiffReplay(state);
207 }
208
209 private void restoreWithDiffReplay(long state) {
210 var modelDiffCursor = getDiffCursor(state);
211 for (AnyDataRepresentation anyDataRepresentation : this.getDataRepresentations()) {
212 var dataRepresentation = (DataRepresentation<?, ?>) anyDataRepresentation;
213 restoreRepresentationWithDiffReplay(modelDiffCursor, dataRepresentation);
214 }
215 }
216
217 private <K, V> void restoreRepresentationWithDiffReplay(ModelDiffCursor modelDiffCursor,
218 DataRepresentation<K, V> dataRepresentation) {
219 DiffCursor<K, V> diffCursor = modelDiffCursor.getCursor(dataRepresentation);
220 this.putAll(dataRepresentation, diffCursor);
221 }
222}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java
index ffd5f6de..e0bca9e0 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java
@@ -2,10 +2,10 @@ package tools.refinery.store.query.viatra.internal.cardinality;
2 2
3import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator; 3import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator;
4import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; 4import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
5import tools.refinery.store.model.representation.cardinality.FiniteUpperCardinality; 5import tools.refinery.store.representation.cardinality.FiniteUpperCardinality;
6import tools.refinery.store.model.representation.cardinality.UnboundedUpperCardinality; 6import tools.refinery.store.representation.cardinality.UnboundedUpperCardinality;
7import tools.refinery.store.model.representation.cardinality.UpperCardinalities; 7import tools.refinery.store.representation.cardinality.UpperCardinalities;
8import tools.refinery.store.model.representation.cardinality.UpperCardinality; 8import tools.refinery.store.representation.cardinality.UpperCardinality;
9 9
10import java.util.stream.Stream; 10import java.util.stream.Stream;
11 11
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
index d32d49ba..4eb8898b 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
@@ -5,7 +5,7 @@ import org.eclipse.viatra.query.runtime.api.scope.IEngineContext;
5import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; 5import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
6 6
7import tools.refinery.store.model.Model; 7import tools.refinery.store.model.Model;
8import tools.refinery.store.query.viatra.internal.viewupdate.ModelUpdateListener; 8import tools.refinery.store.query.viatra.internal.update.ModelUpdateListener;
9 9
10public class RelationalEngineContext implements IEngineContext { 10public class RelationalEngineContext implements IEngineContext {
11 private final IBaseIndex baseIndex = new DummyBaseIndexer(); 11 private final IBaseIndex baseIndex = new DummyBaseIndexer();
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
index eb3c6fbd..47b83634 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
@@ -39,7 +39,7 @@ public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
39 39
40 public void ensureValidKey(IInputKey key) { 40 public void ensureValidKey(IInputKey key) {
41 if (!(key instanceof RelationViewWrapper)) { 41 if (!(key instanceof RelationViewWrapper)) {
42 throw new IllegalArgumentException("The input key %s is not a valid input key.".formatted(key)); 42 throw new IllegalArgumentException("The input key %s is not a valid input key".formatted(key));
43 } 43 }
44 } 44 }
45} 45}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
index e01525e0..7375b240 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
@@ -8,7 +8,7 @@ import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
8import org.eclipse.viatra.query.runtime.matchers.util.Accuracy; 8import org.eclipse.viatra.query.runtime.matchers.util.Accuracy;
9import tools.refinery.store.model.Model; 9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper; 10import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper;
11import tools.refinery.store.query.viatra.internal.viewupdate.ModelUpdateListener; 11import tools.refinery.store.query.viatra.internal.update.ModelUpdateListener;
12import tools.refinery.store.query.view.AnyRelationView; 12import tools.refinery.store.query.view.AnyRelationView;
13import tools.refinery.store.query.view.RelationView; 13import tools.refinery.store.query.view.RelationView;
14 14
@@ -48,12 +48,12 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
48 48
49 @Override 49 @Override
50 public boolean isCoalescing() { 50 public boolean isCoalescing() {
51 return true; 51 return false;
52 } 52 }
53 53
54 @Override 54 @Override
55 public boolean isIndexed(IInputKey key, IndexingService service) { 55 public boolean isIndexed(IInputKey key, IndexingService service) {
56 if (key instanceof RelationView<?> relationalKey) { 56 if (key instanceof AnyRelationView relationalKey) {
57 return this.modelUpdateListener.containsRelationView(relationalKey); 57 return this.modelUpdateListener.containsRelationView(relationalKey);
58 } else { 58 } else {
59 return false; 59 return false;
@@ -63,7 +63,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
63 @Override 63 @Override
64 public void ensureIndexed(IInputKey key, IndexingService service) { 64 public void ensureIndexed(IInputKey key, IndexingService service) {
65 if (!isIndexed(key, service)) { 65 if (!isIndexed(key, service)) {
66 throw new IllegalStateException("Engine tries to index a new key " + key); 66 throw new IllegalStateException("Engine tries to index a new key %s".formatted(key));
67 } 67 }
68 } 68 }
69 69
@@ -73,7 +73,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
73 if (modelUpdateListener.containsRelationView(relationViewKey)) { 73 if (modelUpdateListener.containsRelationView(relationViewKey)) {
74 return relationViewKey; 74 return relationViewKey;
75 } else { 75 } else {
76 throw new IllegalStateException("Query is asking for non-indexed key"); 76 throw new IllegalStateException("Query is asking for non-indexed key %s".formatted(relationViewKey));
77 } 77 }
78 } else { 78 } else {
79 throw new IllegalStateException("Query is asking for non-relational key"); 79 throw new IllegalStateException("Query is asking for non-relational key");
@@ -131,7 +131,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
131 131
132 @Override 132 @Override
133 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { 133 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
134 var relationViewKey = (RelationView<?>) checkKey(key); 134 var relationViewKey = checkKey(key);
135 this.modelUpdateListener.addListener(key, relationViewKey, seed, listener); 135 this.modelUpdateListener.addListener(key, relationViewKey, seed, listener);
136 136
137 } 137 }
@@ -168,7 +168,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
168 } 168 }
169 169
170 @Override 170 @Override
171 public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException { 171 public void executeAfterTraversal(Runnable runnable) {
172 runnable.run(); 172 runnable.run();
173 } 173 }
174} 174}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
index aa3fba6e..2b5618d2 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
@@ -1,5 +1,6 @@
1package tools.refinery.store.query.viatra.internal.pquery; 1package tools.refinery.store.query.viatra.internal.pquery;
2 2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
3import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; 4import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
4import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; 5import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
5import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; 6import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality;
@@ -20,20 +21,28 @@ import tools.refinery.store.query.atom.*;
20import tools.refinery.store.query.view.AnyRelationView; 21import tools.refinery.store.query.view.AnyRelationView;
21 22
22import java.util.*; 23import java.util.*;
24import java.util.function.Function;
23import java.util.stream.Collectors; 25import java.util.stream.Collectors;
24 26
25public class DNF2PQuery { 27public class DNF2PQuery {
26 private final Set<DNF> translating = new LinkedHashSet<>(); 28 private final Set<DNF> translating = new LinkedHashSet<>();
27 29
28 private final Map<DNF, SimplePQuery> dnf2PQueryMap = new HashMap<>(); 30 private final Map<DNF, RawPQuery> dnf2PQueryMap = new HashMap<>();
29 31
30 private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new HashMap<>(); 32 private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new LinkedHashMap<>();
31 33
32 public SimplePQuery translate(DNF dnfQuery) { 34 private Function<DNF, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null,
35 QueryEvaluationHint.BackendRequirement.UNSPECIFIED);
36
37 public void setComputeHint(Function<DNF, QueryEvaluationHint> computeHint) {
38 this.computeHint = computeHint;
39 }
40
41 public RawPQuery translate(DNF dnfQuery) {
33 if (translating.contains(dnfQuery)) { 42 if (translating.contains(dnfQuery)) {
34 var path = translating.stream().map(DNF::getName).collect(Collectors.joining(" -> ")); 43 var path = translating.stream().map(DNF::name).collect(Collectors.joining(" -> "));
35 throw new IllegalStateException("Circular reference %s -> %s detected".formatted(path, 44 throw new IllegalStateException("Circular reference %s -> %s detected".formatted(path,
36 dnfQuery.getName())); 45 dnfQuery.name()));
37 } 46 }
38 // We can't use computeIfAbsent here, because translating referenced queries calls this method in a reentrant 47 // We can't use computeIfAbsent here, because translating referenced queries calls this method in a reentrant
39 // way, which would cause a ConcurrentModificationException with computeIfAbsent. 48 // way, which would cause a ConcurrentModificationException with computeIfAbsent.
@@ -50,8 +59,17 @@ public class DNF2PQuery {
50 return pQuery; 59 return pQuery;
51 } 60 }
52 61
53 private SimplePQuery doTranslate(DNF dnfQuery) { 62 public Collection<AnyRelationView> getRelationViews() {
54 var pQuery = new SimplePQuery(dnfQuery.getUniqueName()); 63 return Collections.unmodifiableCollection(view2WrapperMap.keySet());
64 }
65
66 public RawPQuery getAlreadyTranslated(DNF dnfQuery) {
67 return dnf2PQueryMap.get(dnfQuery);
68 }
69
70 private RawPQuery doTranslate(DNF dnfQuery) {
71 var pQuery = new RawPQuery(dnfQuery.getUniqueName());
72 pQuery.setEvaluationHints(computeHint.apply(dnfQuery));
55 73
56 Map<Variable, PParameter> parameters = new HashMap<>(); 74 Map<Variable, PParameter> parameters = new HashMap<>();
57 for (Variable variable : dnfQuery.getParameters()) { 75 for (Variable variable : dnfQuery.getParameters()) {
@@ -86,7 +104,7 @@ public class DNF2PQuery {
86 translateEquivalenceAtom(equivalenceAtom, body); 104 translateEquivalenceAtom(equivalenceAtom, body);
87 } else if (constraint instanceof RelationViewAtom relationViewAtom) { 105 } else if (constraint instanceof RelationViewAtom relationViewAtom) {
88 translateRelationViewAtom(relationViewAtom, body); 106 translateRelationViewAtom(relationViewAtom, body);
89 } else if (constraint instanceof CallAtom<?> callAtom) { 107 } else if (constraint instanceof DNFCallAtom callAtom) {
90 translateCallAtom(callAtom, body); 108 translateCallAtom(callAtom, body);
91 } else if (constraint instanceof ConstantAtom constantAtom) { 109 } else if (constraint instanceof ConstantAtom constantAtom) {
92 translateConstantAtom(constantAtom, body); 110 translateConstantAtom(constantAtom, body);
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SimplePQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
index a367cbf2..5d0b9e82 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SimplePQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
@@ -3,27 +3,24 @@ package tools.refinery.store.query.viatra.internal.pquery;
3import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; 3import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; 4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
5import org.eclipse.viatra.query.runtime.api.scope.QueryScope; 5import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
6import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
7import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; 6import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
8import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery; 7import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery;
9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 8import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
11import tools.refinery.store.query.viatra.internal.RawPatternMatcher;
12import tools.refinery.store.query.viatra.internal.RelationalScope; 10import tools.refinery.store.query.viatra.internal.RelationalScope;
13 11
14import java.util.LinkedHashSet; 12import java.util.LinkedHashSet;
15import java.util.List; 13import java.util.List;
16import java.util.Set; 14import java.util.Set;
17 15
18public class SimplePQuery extends BasePQuery { 16public class RawPQuery extends BasePQuery {
19 private final String fullyQualifiedName; 17 private final String fullyQualifiedName;
20 private List<PParameter> parameters; 18 private List<PParameter> parameters;
21 private final LinkedHashSet<PBody> bodies = new LinkedHashSet<>(); 19 private final LinkedHashSet<PBody> bodies = new LinkedHashSet<>();
22 20
23 public SimplePQuery(String name) { 21 public RawPQuery(String name) {
24 super(PVisibility.PUBLIC); 22 super(PVisibility.PUBLIC);
25 fullyQualifiedName = name; 23 fullyQualifiedName = name;
26 setEvaluationHints(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.UNSPECIFIED));
27 } 24 }
28 25
29 @Override 26 @Override
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
index 2c488319..e944e873 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RawPatternMatcher.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
@@ -1,7 +1,8 @@
1package tools.refinery.store.query.viatra.internal; 1package tools.refinery.store.query.viatra.internal.pquery;
2 2
3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher; 3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; 4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
5import tools.refinery.store.query.ResultSet;
5import tools.refinery.store.query.viatra.ViatraTupleLike; 6import tools.refinery.store.query.viatra.ViatraTupleLike;
6import tools.refinery.store.tuple.Tuple; 7import tools.refinery.store.tuple.Tuple;
7import tools.refinery.store.tuple.TupleLike; 8import tools.refinery.store.tuple.TupleLike;
@@ -9,7 +10,7 @@ import tools.refinery.store.tuple.TupleLike;
9import java.util.Optional; 10import java.util.Optional;
10import java.util.stream.Stream; 11import java.util.stream.Stream;
11 12
12public class RawPatternMatcher extends GenericPatternMatcher { 13public class RawPatternMatcher extends GenericPatternMatcher implements ResultSet {
13 protected final Object[] empty; 14 protected final Object[] empty;
14 15
15 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) { 16 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
@@ -17,34 +18,42 @@ public class RawPatternMatcher extends GenericPatternMatcher {
17 empty = new Object[specification.getParameterNames().size()]; 18 empty = new Object[specification.getParameterNames().size()];
18 } 19 }
19 20
21 @Override
20 public boolean hasResult() { 22 public boolean hasResult() {
21 return backend.hasMatch(empty); 23 return backend.hasMatch(empty);
22 } 24 }
23 25
26 @Override
24 public boolean hasResult(Tuple parameters) { 27 public boolean hasResult(Tuple parameters) {
25 return backend.hasMatch(toParametersArray(parameters)); 28 return backend.hasMatch(toParametersArray(parameters));
26 } 29 }
27 30
31 @Override
28 public Optional<TupleLike> oneResult() { 32 public Optional<TupleLike> oneResult() {
29 return backend.getOneArbitraryMatch(empty).map(ViatraTupleLike::new); 33 return backend.getOneArbitraryMatch(empty).map(ViatraTupleLike::new);
30 } 34 }
31 35
36 @Override
32 public Optional<TupleLike> oneResult(Tuple parameters) { 37 public Optional<TupleLike> oneResult(Tuple parameters) {
33 return backend.getOneArbitraryMatch(toParametersArray(parameters)).map(ViatraTupleLike::new); 38 return backend.getOneArbitraryMatch(toParametersArray(parameters)).map(ViatraTupleLike::new);
34 } 39 }
35 40
41 @Override
36 public Stream<TupleLike> allResults() { 42 public Stream<TupleLike> allResults() {
37 return backend.getAllMatches(empty).map(ViatraTupleLike::new); 43 return backend.getAllMatches(empty).map(ViatraTupleLike::new);
38 } 44 }
39 45
46 @Override
40 public Stream<TupleLike> allResults(Tuple parameters) { 47 public Stream<TupleLike> allResults(Tuple parameters) {
41 return backend.getAllMatches(toParametersArray(parameters)).map(ViatraTupleLike::new); 48 return backend.getAllMatches(toParametersArray(parameters)).map(ViatraTupleLike::new);
42 } 49 }
43 50
51 @Override
44 public int countResults() { 52 public int countResults() {
45 return backend.countMatches(empty); 53 return backend.countMatches(empty);
46 } 54 }
47 55
56 @Override
48 public int countResults(Tuple parameters) { 57 public int countResults(Tuple parameters) {
49 return backend.countMatches(toParametersArray(parameters)); 58 return backend.countMatches(toParametersArray(parameters));
50 } 59 }
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java
index e48648bf..c442add8 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java
@@ -10,7 +10,7 @@ public class RelationViewWrapper extends BaseInputKeyWrapper<AnyRelationView> {
10 10
11 @Override 11 @Override
12 public String getPrettyPrintableName() { 12 public String getPrettyPrintableName() {
13 return wrappedKey.getName(); 13 return wrappedKey.name();
14 } 14 }
15 15
16 @Override 16 @Override
@@ -20,7 +20,7 @@ public class RelationViewWrapper extends BaseInputKeyWrapper<AnyRelationView> {
20 20
21 @Override 21 @Override
22 public int getArity() { 22 public int getArity() {
23 return wrappedKey.getArity(); 23 return wrappedKey.arity();
24 } 24 }
25 25
26 @Override 26 @Override
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
new file mode 100644
index 00000000..1ae3daa7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
@@ -0,0 +1,46 @@
1package tools.refinery.store.query.viatra.internal.update;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import tools.refinery.store.model.Model;
7import tools.refinery.store.query.view.AnyRelationView;
8import tools.refinery.store.query.view.RelationView;
9
10import java.util.Collection;
11import java.util.HashMap;
12import java.util.Map;
13
14public class ModelUpdateListener {
15 private final Map<AnyRelationView, RelationViewUpdateListener<?>> relationViewUpdateListeners;
16
17 public ModelUpdateListener(Model model, Collection<AnyRelationView> relationViews) {
18 relationViewUpdateListeners = new HashMap<>(relationViews.size());
19 for (var relationView : relationViews) {
20 registerView(model, (RelationView<?>) relationView);
21 }
22 }
23
24 private <T> void registerView(Model model, RelationView<T> relationView) {
25 var listener = RelationViewUpdateListener.of(relationView);
26 var interpretation = model.getInterpretation(relationView.getSymbol());
27 interpretation.addListener(listener, true);
28 relationViewUpdateListeners.put(relationView, listener);
29 }
30
31 public boolean containsRelationView(AnyRelationView relationView) {
32 return relationViewUpdateListeners.containsKey(relationView);
33 }
34
35 public void addListener(IInputKey key, AnyRelationView relationView, ITuple seed,
36 IQueryRuntimeContextListener listener) {
37 var relationViewUpdateListener = relationViewUpdateListeners.get(relationView);
38 relationViewUpdateListener.addFilter(key, seed, listener);
39 }
40
41 public void removeListener(IInputKey key, AnyRelationView relationView, ITuple seed,
42 IQueryRuntimeContextListener listener) {
43 var relationViewUpdateListener = relationViewUpdateListeners.get(relationView);
44 relationViewUpdateListener.removeFilter(key, seed, listener);
45 }
46}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java
new file mode 100644
index 00000000..221f1b4a
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java
@@ -0,0 +1,66 @@
1package tools.refinery.store.query.viatra.internal.update;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
7
8import java.util.Arrays;
9import java.util.Objects;
10
11public final class RelationViewFilter {
12 private final IInputKey inputKey;
13 private final Object[] seed;
14 private final IQueryRuntimeContextListener listener;
15
16 public RelationViewFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
17 this.inputKey = inputKey;
18 this.seed = seedToArray(seed);
19 this.listener = listener;
20 }
21
22 public void update(Tuple updateTuple, boolean isInsertion) {
23 if (isMatching(updateTuple)) {
24 listener.update(inputKey, updateTuple, isInsertion);
25 }
26 }
27
28 private boolean isMatching(ITuple tuple) {
29 if (seed == null) {
30 return true;
31 }
32 int size = seed.length;
33 for (int i = 0; i < size; i++) {
34 var filterElement = seed[i];
35 if (filterElement != null && !filterElement.equals(tuple.get(i))) {
36 return false;
37 }
38 }
39 return true;
40 }
41
42 // Use <code>null</code> instead of an empty array to speed up comparisons.
43 @SuppressWarnings("squid:S1168")
44 private static Object[] seedToArray(ITuple seed) {
45 for (var element : seed.getElements()) {
46 if (element != null) {
47 return seed.getElements();
48 }
49 }
50 return null;
51 }
52
53 @Override
54 public boolean equals(Object obj) {
55 if (obj == this) return true;
56 if (obj == null || obj.getClass() != this.getClass()) return false;
57 var that = (RelationViewFilter) obj;
58 return Objects.equals(this.inputKey, that.inputKey) && Arrays.equals(this.seed, that.seed) &&
59 Objects.equals(this.listener, that.listener);
60 }
61
62 @Override
63 public int hashCode() {
64 return Objects.hash(inputKey, Arrays.hashCode(seed), listener);
65 }
66}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java
new file mode 100644
index 00000000..e0d44e34
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java
@@ -0,0 +1,40 @@
1package tools.refinery.store.query.viatra.internal.update;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
7import tools.refinery.store.model.InterpretationListener;
8import tools.refinery.store.query.view.RelationView;
9import tools.refinery.store.query.view.TuplePreservingRelationView;
10
11import java.util.ArrayList;
12import java.util.List;
13
14public abstract class RelationViewUpdateListener<T> implements InterpretationListener<T> {
15 private final List<RelationViewFilter> filters = new ArrayList<>();
16
17 public void addFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
18 filters.add(new RelationViewFilter(inputKey, seed, listener));
19 }
20
21 public void removeFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
22 filters.remove(new RelationViewFilter(inputKey, seed, listener));
23 }
24
25 protected void processUpdate(Tuple tuple, boolean isInsertion) {
26 int size = filters.size();
27 // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead.
28 //noinspection ForLoopReplaceableByForEach
29 for (int i = 0; i < size; i++) {
30 filters.get(i).update(tuple, isInsertion);
31 }
32 }
33
34 public static <T> RelationViewUpdateListener<T> of(RelationView<T> relationView) {
35 if (relationView instanceof TuplePreservingRelationView<T> tuplePreservingRelationView) {
36 return new TuplePreservingRelationViewUpdateListener<>(tuplePreservingRelationView);
37 }
38 return new TupleChangingRelationViewUpdateListener<>(relationView);
39 }
40}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java
new file mode 100644
index 00000000..c17e826d
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java
@@ -0,0 +1,35 @@
1package tools.refinery.store.query.viatra.internal.update;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
4import tools.refinery.store.query.view.RelationView;
5import tools.refinery.store.tuple.Tuple;
6
7import java.util.Arrays;
8
9public class TupleChangingRelationViewUpdateListener<T> extends RelationViewUpdateListener<T> {
10 private final RelationView<T> relationView;
11
12 TupleChangingRelationViewUpdateListener(RelationView<T> relationView) {
13 this.relationView = relationView;
14 }
15
16 @Override
17 public void put(Tuple key, T fromValue, T toValue, boolean restoring) {
18 boolean fromPresent = relationView.filter(key, fromValue);
19 boolean toPresent = relationView.filter(key, toValue);
20 if (fromPresent) {
21 if (toPresent) { // value change
22 var fromArray = relationView.forwardMap(key, fromValue);
23 var toArray = relationView.forwardMap(key, toValue);
24 if (!Arrays.equals(fromArray, toArray)) {
25 processUpdate(Tuples.flatTupleOf(fromArray), false);
26 processUpdate(Tuples.flatTupleOf(toArray), true);
27 }
28 } else { // fromValue disappears
29 processUpdate(Tuples.flatTupleOf(relationView.forwardMap(key, fromValue)), false);
30 }
31 } else if (toPresent) { // toValue disappears
32 processUpdate(Tuples.flatTupleOf(relationView.forwardMap(key, toValue)), true);
33 }
34 }
35}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java
new file mode 100644
index 00000000..9c3ef61c
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java
@@ -0,0 +1,24 @@
1package tools.refinery.store.query.viatra.internal.update;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
4import tools.refinery.store.query.view.TuplePreservingRelationView;
5import tools.refinery.store.tuple.Tuple;
6
7public class TuplePreservingRelationViewUpdateListener<T> extends RelationViewUpdateListener<T> {
8 private final TuplePreservingRelationView<T> view;
9
10 TuplePreservingRelationViewUpdateListener(TuplePreservingRelationView<T> view) {
11 this.view = view;
12 }
13
14 @Override
15 public void put(Tuple key, T fromValue, T toValue, boolean restoring) {
16 boolean fromPresent = view.filter(key, fromValue);
17 boolean toPresent = view.filter(key, toValue);
18 if (fromPresent == toPresent) {
19 return;
20 }
21 var translated = Tuples.flatTupleOf(view.forwardMap(key));
22 processUpdate(translated, toPresent);
23 }
24}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java
deleted file mode 100644
index 6a1d06a9..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java
+++ /dev/null
@@ -1,112 +0,0 @@
1package tools.refinery.store.query.viatra.internal.viewupdate;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import tools.refinery.store.model.representation.AnyRelation;
7import tools.refinery.store.query.view.AnyRelationView;
8import tools.refinery.store.tuple.Tuple;
9import tools.refinery.store.model.representation.Relation;
10import tools.refinery.store.query.view.RelationView;
11
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.Set;
16
17public class ModelUpdateListener {
18 /**
19 * Collections of Relations and their Views.
20 */
21 private final Map<AnyRelation, Set<AnyRelationView>> relation2View;
22
23 /**
24 * Collection of Views and their buffers.
25 */
26 private final Map<AnyRelationView, Set<ViewUpdateBuffer<?>>> view2Buffers;
27
28 public ModelUpdateListener(Set<AnyRelationView> relationViews) {
29 this.relation2View = new HashMap<>();
30 this.view2Buffers = new HashMap<>();
31
32 for (var relationView : relationViews) {
33 registerView(relationView);
34 }
35 }
36
37 private void registerView(AnyRelationView view) {
38 AnyRelation relation = view.getRepresentation();
39
40 // 1. register views to relations, if necessary
41 var views = relation2View.computeIfAbsent(relation, x -> new HashSet<>());
42 views.add(view);
43
44 // 2. register notifier map to views, if necessary
45 view2Buffers.computeIfAbsent(view, x -> new HashSet<>());
46 }
47
48 public boolean containsRelationView(AnyRelationView relationalKey) {
49 return view2Buffers.containsKey(relationalKey);
50 }
51
52 public <D> void addListener(IInputKey key, RelationView<D> relationView, ITuple seed,
53 IQueryRuntimeContextListener listener) {
54 if (view2Buffers.containsKey(relationView)) {
55 ViewUpdateTranslator<D> updateListener = new ViewUpdateTranslator<>(key, relationView, seed, listener);
56 ViewUpdateBuffer<D> updateBuffer = new ViewUpdateBuffer<>(updateListener);
57 view2Buffers.get(relationView).add(updateBuffer);
58 } else {
59 throw new IllegalArgumentException();
60 }
61 }
62
63 public void removeListener(IInputKey key, AnyRelationView relationView, ITuple seed,
64 IQueryRuntimeContextListener listener) {
65 if (view2Buffers.containsKey(relationView)) {
66 Set<ViewUpdateBuffer<?>> buffers = this.view2Buffers.get(relationView);
67 for (var buffer : buffers) {
68 if (buffer.getUpdateListener().equals(key, relationView, seed, listener)) {
69 // remove buffer and terminate immediately, or it will break iterator.
70 buffers.remove(buffer);
71 return;
72 }
73 }
74 } else {
75 throw new IllegalArgumentException("Relation view is not registered for updates");
76 }
77 }
78
79 public <D> void addUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
80 var views = this.relation2View.get(relation);
81 if (views == null) {
82 return;
83 }
84 for (var view : views) {
85 var buffers = this.view2Buffers.get(view);
86 for (var buffer : buffers) {
87 @SuppressWarnings("unchecked")
88 var typedBuffer = (ViewUpdateBuffer<D>) buffer;
89 typedBuffer.addChange(key, oldValue, newValue);
90 }
91 }
92 }
93
94 public boolean hasChanges() {
95 for (var bufferCollection : this.view2Buffers.values()) {
96 for (ViewUpdateBuffer<?> buffer : bufferCollection) {
97 if (buffer.hasChanges()) {
98 return true;
99 }
100 }
101 }
102 return false;
103 }
104
105 public void flush() {
106 for (var bufferCollection : this.view2Buffers.values()) {
107 for (ViewUpdateBuffer<?> buffer : bufferCollection) {
108 buffer.flush();
109 }
110 }
111 }
112}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdate.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdate.java
deleted file mode 100644
index b9406018..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdate.java
+++ /dev/null
@@ -1,32 +0,0 @@
1package tools.refinery.store.query.viatra.internal.viewupdate;
2
3import java.util.Arrays;
4import java.util.Objects;
5
6record ViewUpdate(Object[] tuple, boolean isInsertion) {
7 @Override
8 public int hashCode() {
9 final int prime = 31;
10 int result = 1;
11 result = prime * result + Arrays.deepHashCode(tuple);
12 result = prime * result + Objects.hash(isInsertion);
13 return result;
14 }
15
16 @Override
17 public boolean equals(Object obj) {
18 if (this == obj)
19 return true;
20 if (obj == null)
21 return false;
22 if (getClass() != obj.getClass())
23 return false;
24 ViewUpdate other = (ViewUpdate) obj;
25 return isInsertion == other.isInsertion && Arrays.deepEquals(tuple, other.tuple);
26 }
27
28 @Override
29 public String toString() {
30 return "ViewUpdate [" + Arrays.toString(tuple) + "insertion= " + this.isInsertion + "]";
31 }
32}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateBuffer.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateBuffer.java
deleted file mode 100644
index 49f4c501..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateBuffer.java
+++ /dev/null
@@ -1,47 +0,0 @@
1package tools.refinery.store.query.viatra.internal.viewupdate;
2
3import tools.refinery.store.tuple.Tuple;
4
5import java.util.ArrayList;
6import java.util.Arrays;
7import java.util.List;
8
9public class ViewUpdateBuffer<D> {
10 protected final ViewUpdateTranslator<D> updateListener;
11
12 protected final List<ViewUpdate> buffer = new ArrayList<>();
13
14 public ViewUpdateBuffer(ViewUpdateTranslator<D> updateListener) {
15 this.updateListener = updateListener;
16 }
17
18 public ViewUpdateTranslator<D> getUpdateListener() {
19 return updateListener;
20 }
21
22 public boolean hasChanges() {
23 return !buffer.isEmpty();
24 }
25
26 public void addChange(Tuple tuple, D oldValue, D newValue) {
27 if (oldValue != newValue) {
28 Object[] oldTuple = updateListener.isMatching(tuple, oldValue);
29 Object[] newTuple = updateListener.isMatching(tuple, newValue);
30 if (!Arrays.equals(oldTuple, newTuple)) {
31 if (oldTuple != null) {
32 buffer.add(new ViewUpdate(oldTuple, false));
33 }
34 if (newTuple != null) {
35 buffer.add(new ViewUpdate(newTuple, true));
36 }
37 }
38 }
39 }
40
41 public void flush() {
42 for (ViewUpdate viewChange : buffer) {
43 updateListener.processChange(viewChange);
44 }
45 buffer.clear();
46 }
47}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateTranslator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateTranslator.java
deleted file mode 100644
index c324c84a..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateTranslator.java
+++ /dev/null
@@ -1,73 +0,0 @@
1package tools.refinery.store.query.viatra.internal.viewupdate;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
7import tools.refinery.store.query.view.AnyRelationView;
8import tools.refinery.store.query.view.RelationView;
9import tools.refinery.store.tuple.Tuple;
10
11import java.util.Objects;
12
13public class ViewUpdateTranslator<D> {
14 private final IInputKey wrappedKey;
15
16 private final RelationView<D> key;
17
18 private final ITuple filter;
19
20 private final IQueryRuntimeContextListener listener;
21
22 public ViewUpdateTranslator(IInputKey wrappedKey, RelationView<D> key, ITuple filter,
23 IQueryRuntimeContextListener listener) {
24 super();
25 this.wrappedKey = wrappedKey;
26 this.key = key;
27 this.filter = filter;
28 this.listener = listener;
29 }
30
31 public boolean equals(IInputKey wrappedKey, AnyRelationView relationView, ITuple seed,
32 IQueryRuntimeContextListener listener) {
33 return this.wrappedKey == wrappedKey && key == relationView && filter.equals(seed) && this.listener == listener;
34 }
35
36 public void processChange(ViewUpdate change) {
37 listener.update(wrappedKey, Tuples.flatTupleOf(change.tuple()), change.isInsertion());
38 }
39
40 @SuppressWarnings("squid:S1168")
41 public Object[] isMatching(Tuple tuple, D value) {
42 if (!key.filter(tuple, value)) {
43 return null;
44 }
45 return isMatching(key.forwardMap(tuple, value), filter);
46 }
47
48 @SuppressWarnings("squid:S1168")
49 private Object[] isMatching(Object[] tuple, ITuple filter) {
50 for (int i = 0; i < filter.getSize(); i++) {
51 final Object filterObject = filter.get(i);
52 if (filterObject != null && !filterObject.equals(tuple[i])) {
53 return null;
54 }
55 }
56 return tuple;
57 }
58
59 @Override
60 public int hashCode() {
61 return Objects.hash(filter, key, listener);
62 }
63
64 @Override
65 public boolean equals(Object obj) {
66 if (this == obj)
67 return true;
68 if (!(obj instanceof ViewUpdateTranslator<?> other))
69 return false;
70 return Objects.equals(filter, other.filter) && Objects.equals(key, other.key)
71 && Objects.equals(listener, other.listener);
72 }
73}