1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
package tools.refinery.store.query.viatra.internal.viewupdate;
import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
import tools.refinery.store.model.Tuple;
import tools.refinery.store.model.representation.Relation;
import tools.refinery.store.query.view.RelationView;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class ModelUpdateListener {
/**
* Collections of Relations and their Views.
*/
private final Map<Relation<?>, Set<RelationView<?>>> relation2View;
/**
* Collection of Views and their buffers.
*/
private final Map<RelationView<?>, Set<ViewUpdateBuffer<?>>> view2Buffers;
public ModelUpdateListener(Set<RelationView<?>> relationViews) {
this.relation2View = new HashMap<>();
this.view2Buffers = new HashMap<>();
for (RelationView<?> relationView : relationViews) {
registerView(relationView);
}
}
private void registerView(RelationView<?> view) {
Relation<?> relation = view.getRepresentation();
// 1. register views to relations, if necessary
var views = relation2View.computeIfAbsent(relation, x -> new HashSet<>());
views.add(view);
// 2. register notifier map to views, if necessary
view2Buffers.computeIfAbsent(view, x -> new HashSet<>());
}
public boolean containsRelationalView(RelationView<?> relationalKey) {
return view2Buffers.containsKey(relationalKey);
}
public <D> void addListener(RelationView<D> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
if (view2Buffers.containsKey(relationView)) {
ViewUpdateTranslator<D> updateListener = new ViewUpdateTranslator<>(relationView, seed, listener);
ViewUpdateBuffer<D> updateBuffer = new ViewUpdateBuffer<>(updateListener);
view2Buffers.get(relationView).add(updateBuffer);
} else
throw new IllegalArgumentException();
}
public void removeListener(RelationView<?> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
if (view2Buffers.containsKey(relationView)) {
Set<ViewUpdateBuffer<?>> buffers = this.view2Buffers.get(relationView);
for (var buffer : buffers) {
if (buffer.getUpdateListener().equals(relationView, seed, listener)) {
// remove buffer and terminate immediately, or it will break iterator.
buffers.remove(buffer);
return;
}
}
} else {
throw new IllegalArgumentException("Relation view is not registered for updates");
}
}
public <D> void addUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
var views = this.relation2View.get(relation);
if (views == null) {
return;
}
for (var view : views) {
var buffers = this.view2Buffers.get(view);
for (var buffer : buffers) {
@SuppressWarnings("unchecked")
var typedBuffer = (ViewUpdateBuffer<D>) buffer;
typedBuffer.addChange(key, oldValue, newValue);
}
}
}
public boolean hasChanges() {
for (var bufferCollection : this.view2Buffers.values()) {
for (ViewUpdateBuffer<?> buffer : bufferCollection) {
if (buffer.hasChanges())
return true;
}
}
return false;
}
public void flush() {
for (var bufferCollection : this.view2Buffers.values()) {
for (ViewUpdateBuffer<?> buffer : bufferCollection) {
buffer.flush();
}
}
}
}
|