aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java
blob: 2591988890ec74e28587f7f2a7462f49c0066c90 (plain) (blame)
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();
			}
		}
	}
}