aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
blob: 10331e28bc0311516d4624388700ddaa0e3ae99b (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package tools.refinery.store.model.internal;

import tools.refinery.store.adapter.AdapterList;
import tools.refinery.store.adapter.AnyModelAdapterType;
import tools.refinery.store.adapter.ModelAdapter;
import tools.refinery.store.adapter.ModelAdapterType;
import tools.refinery.store.map.DiffCursor;
import tools.refinery.store.model.*;
import tools.refinery.store.representation.AnySymbol;
import tools.refinery.store.representation.Symbol;
import tools.refinery.store.tuple.Tuple;

import java.util.*;

public class ModelImpl implements Model {
	private final ModelStore store;
	private long state;
	private Map<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations;
	private final AdapterList<ModelAdapter> adapters;
	private final List<ModelListener> listeners = new ArrayList<>();
	private ModelAction pendingAction = ModelAction.NONE;
	private long restoringToState = -1;

	ModelImpl(ModelStore store, long state, int adapterCount) {
		this.store = store;
		this.state = state;
		adapters = new AdapterList<>(adapterCount);
	}

	void setInterpretations(Map<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations) {
		this.interpretations = interpretations;
	}

	@Override
	public ModelStore getStore() {
		return store;
	}

	@Override
	public long getState() {
		return state;
	}

	@Override
	public <T> Interpretation<T> getInterpretation(Symbol<T> symbol) {
		var interpretation = interpretations.get(symbol);
		if (interpretation == null) {
			throw new IllegalArgumentException("No interpretation for symbol %s in model".formatted(symbol));
		}
		@SuppressWarnings("unchecked")
		var typedInterpretation = (Interpretation<T>) interpretation;
		return typedInterpretation;
	}

	@Override
	public ModelDiffCursor getDiffCursor(long to) {
		var diffCursors = new HashMap<AnySymbol, DiffCursor<Tuple, ?>>(interpretations.size());
		for (var entry : interpretations.entrySet()) {
			diffCursors.put(entry.getKey(), entry.getValue().getDiffCursor(to));
		}
		return new ModelDiffCursor(diffCursors);
	}

	@Override
	public long commit() {
		if (pendingAction != ModelAction.NONE) {
			throw pendingActionError("commit");
		}
		pendingAction = ModelAction.COMMIT;
		try {
			int listenerCount = listeners.size();
			int i = listenerCount;
			long version = 0;
			while (i > 0) {
				i--;
				listeners.get(i).beforeCommit();
			}
			boolean versionSet = false;
			for (var interpretation : interpretations.values()) {
				long newVersion = interpretation.commit();
				if (versionSet) {
					if (version != newVersion) {
						throw new IllegalStateException("Interpretations in model have different versions (%d and %d)"
								.formatted(version, newVersion));
					}
				} else {
					version = newVersion;
					versionSet = true;
				}
			}
			state = version;
			while (i < listenerCount) {
				listeners.get(i).afterCommit();
				i++;
			}
			return version;
		} finally {
			pendingAction = ModelAction.NONE;
		}
	}

	@Override
	public void restore(long version) {
		if (pendingAction != ModelAction.NONE) {
			throw pendingActionError("restore to %d".formatted(version));
		}
		if (!store.getStates().contains(version)) {
			throw new IllegalArgumentException("Store does not contain state %d".formatted(version));
		}
		pendingAction = ModelAction.RESTORE;
		restoringToState = version;
		try {
			int listenerCount = listeners.size();
			int i = listenerCount;
			while (i > 0) {
				i--;
				listeners.get(i).beforeRestore(version);
			}
			for (var interpretation : interpretations.values()) {
				interpretation.restore(version);
			}
			state = version;
			while (i < listenerCount) {
				listeners.get(i).afterRestore();
				i++;
			}
		} finally {
			pendingAction = ModelAction.NONE;
			restoringToState = -1;
		}
	}

	public RuntimeException pendingActionError(String currentActionName) {
		var pendingActionName = switch (pendingAction) {
			case NONE -> throw new IllegalArgumentException("Trying to throw pending action error when there is no " +
					"pending action");
			case COMMIT -> "commit";
			case RESTORE -> "restore to %d".formatted(restoringToState);
		};
		return new IllegalStateException("Cannot %s due to pending %s".formatted(currentActionName, pendingActionName));
	}

	@Override
	public <T extends ModelAdapter> Optional<T> tryGetAdapter(ModelAdapterType<? extends T, ?, ?> adapterType) {
		return adapters.tryGet(adapterType, adapterType.getModelAdapterClass());
	}

	@Override
	public <T extends ModelAdapter> T getAdapter(ModelAdapterType<T, ?, ?> adapterType) {
		return adapters.get(adapterType, adapterType.getModelAdapterClass());
	}

	void addAdapter(AnyModelAdapterType adapterType, ModelAdapter adapter) {
		adapters.add(adapterType, adapter);
	}

	@Override
	public void addListener(ModelListener listener) {
		listeners.add(listener);
	}

	@Override
	public void removeListener(ModelListener listener) {
		listeners.remove(listener);
	}
}