diff options
author | Kristóf Marussy <kristof@marussy.com> | 2023-02-02 17:20:35 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2023-02-02 17:20:35 +0100 |
commit | f062947f68e79dc866fc0d8d1f9b8874fd00b51e (patch) | |
tree | 9719e020fd3a9c0fa5a3fe7877937a81dad45b35 /subprojects/store | |
parent | feat: track ModelQuery pending changes (diff) | |
download | refinery-f062947f68e79dc866fc0d8d1f9b8874fd00b51e.tar.gz refinery-f062947f68e79dc866fc0d8d1f9b8874fd00b51e.tar.zst refinery-f062947f68e79dc866fc0d8d1f9b8874fd00b51e.zip |
feat: track uncommitted Model changes
Diffstat (limited to 'subprojects/store')
6 files changed, 67 insertions, 18 deletions
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java index 8d2a4614..6ca1ac7b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java | |||
@@ -9,10 +9,14 @@ import tools.refinery.store.representation.Symbol; | |||
9 | import java.util.Optional; | 9 | import java.util.Optional; |
10 | 10 | ||
11 | public interface Model extends Versioned { | 11 | public interface Model extends Versioned { |
12 | long NO_STATE_ID = -1; | ||
13 | |||
12 | ModelStore getStore(); | 14 | ModelStore getStore(); |
13 | 15 | ||
14 | long getState(); | 16 | long getState(); |
15 | 17 | ||
18 | boolean hasUncommittedChanges(); | ||
19 | |||
16 | default AnyInterpretation getInterpretation(AnySymbol symbol) { | 20 | default AnyInterpretation getInterpretation(AnySymbol symbol) { |
17 | return getInterpretation((Symbol<?>) symbol); | 21 | return getInterpretation((Symbol<?>) symbol); |
18 | } | 22 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java index bc863d4b..2e7e62c3 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java | |||
@@ -12,9 +12,9 @@ import java.util.Set; | |||
12 | public interface ModelStore { | 12 | public interface ModelStore { |
13 | Collection<AnySymbol> getSymbols(); | 13 | Collection<AnySymbol> getSymbols(); |
14 | 14 | ||
15 | Model createModel(); | 15 | Model createEmptyModel(); |
16 | 16 | ||
17 | Model createModel(long state); | 17 | Model createModelForState(long state); |
18 | 18 | ||
19 | Set<Long> getStates(); | 19 | Set<Long> getStates(); |
20 | 20 | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java index 10331e28..9eb438c4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java | |||
@@ -18,8 +18,9 @@ public class ModelImpl implements Model { | |||
18 | private Map<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations; | 18 | private Map<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations; |
19 | private final AdapterList<ModelAdapter> adapters; | 19 | private final AdapterList<ModelAdapter> adapters; |
20 | private final List<ModelListener> listeners = new ArrayList<>(); | 20 | private final List<ModelListener> listeners = new ArrayList<>(); |
21 | private boolean uncommittedChanges; | ||
21 | private ModelAction pendingAction = ModelAction.NONE; | 22 | private ModelAction pendingAction = ModelAction.NONE; |
22 | private long restoringToState = -1; | 23 | private long restoringToState = NO_STATE_ID; |
23 | 24 | ||
24 | ModelImpl(ModelStore store, long state, int adapterCount) { | 25 | ModelImpl(ModelStore store, long state, int adapterCount) { |
25 | this.store = store; | 26 | this.store = store; |
@@ -61,9 +62,29 @@ public class ModelImpl implements Model { | |||
61 | return new ModelDiffCursor(diffCursors); | 62 | return new ModelDiffCursor(diffCursors); |
62 | } | 63 | } |
63 | 64 | ||
65 | private void setState(long state) { | ||
66 | this.state = state; | ||
67 | uncommittedChanges = false; | ||
68 | } | ||
69 | |||
70 | void markAsChanged() { | ||
71 | if (!uncommittedChanges) { | ||
72 | uncommittedChanges = true; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public boolean hasUncommittedChanges() { | ||
78 | return uncommittedChanges; | ||
79 | } | ||
80 | |||
81 | private boolean hasPendingAction() { | ||
82 | return pendingAction != ModelAction.NONE || restoringToState != NO_STATE_ID; | ||
83 | } | ||
84 | |||
64 | @Override | 85 | @Override |
65 | public long commit() { | 86 | public long commit() { |
66 | if (pendingAction != ModelAction.NONE) { | 87 | if (hasPendingAction()) { |
67 | throw pendingActionError("commit"); | 88 | throw pendingActionError("commit"); |
68 | } | 89 | } |
69 | pendingAction = ModelAction.COMMIT; | 90 | pendingAction = ModelAction.COMMIT; |
@@ -88,7 +109,7 @@ public class ModelImpl implements Model { | |||
88 | versionSet = true; | 109 | versionSet = true; |
89 | } | 110 | } |
90 | } | 111 | } |
91 | state = version; | 112 | setState(version); |
92 | while (i < listenerCount) { | 113 | while (i < listenerCount) { |
93 | listeners.get(i).afterCommit(); | 114 | listeners.get(i).afterCommit(); |
94 | i++; | 115 | i++; |
@@ -101,7 +122,7 @@ public class ModelImpl implements Model { | |||
101 | 122 | ||
102 | @Override | 123 | @Override |
103 | public void restore(long version) { | 124 | public void restore(long version) { |
104 | if (pendingAction != ModelAction.NONE) { | 125 | if (hasPendingAction()) { |
105 | throw pendingActionError("restore to %d".formatted(version)); | 126 | throw pendingActionError("restore to %d".formatted(version)); |
106 | } | 127 | } |
107 | if (!store.getStates().contains(version)) { | 128 | if (!store.getStates().contains(version)) { |
@@ -119,14 +140,14 @@ public class ModelImpl implements Model { | |||
119 | for (var interpretation : interpretations.values()) { | 140 | for (var interpretation : interpretations.values()) { |
120 | interpretation.restore(version); | 141 | interpretation.restore(version); |
121 | } | 142 | } |
122 | state = version; | 143 | setState(version); |
123 | while (i < listenerCount) { | 144 | while (i < listenerCount) { |
124 | listeners.get(i).afterRestore(); | 145 | listeners.get(i).afterRestore(); |
125 | i++; | 146 | i++; |
126 | } | 147 | } |
127 | } finally { | 148 | } finally { |
128 | pendingAction = ModelAction.NONE; | 149 | pendingAction = ModelAction.NONE; |
129 | restoringToState = -1; | 150 | restoringToState = NO_STATE_ID; |
130 | } | 151 | } |
131 | } | 152 | } |
132 | 153 | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java index 8aab57af..e8c205e4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java | |||
@@ -6,6 +6,7 @@ import tools.refinery.store.adapter.ModelAdapterType; | |||
6 | import tools.refinery.store.adapter.ModelStoreAdapter; | 6 | import tools.refinery.store.adapter.ModelStoreAdapter; |
7 | import tools.refinery.store.map.DiffCursor; | 7 | import tools.refinery.store.map.DiffCursor; |
8 | import tools.refinery.store.map.VersionedMapStore; | 8 | import tools.refinery.store.map.VersionedMapStore; |
9 | import tools.refinery.store.model.Model; | ||
9 | import tools.refinery.store.model.ModelDiffCursor; | 10 | import tools.refinery.store.model.ModelDiffCursor; |
10 | import tools.refinery.store.model.ModelStore; | 11 | import tools.refinery.store.model.ModelStore; |
11 | import tools.refinery.store.representation.AnySymbol; | 12 | import tools.refinery.store.representation.AnySymbol; |
@@ -27,13 +28,13 @@ public class ModelStoreImpl implements ModelStore { | |||
27 | return Collections.unmodifiableCollection(stores.keySet()); | 28 | return Collections.unmodifiableCollection(stores.keySet()); |
28 | } | 29 | } |
29 | 30 | ||
30 | private ModelImpl createEmptyModel(long state) { | 31 | private ModelImpl createModelWithoutInterpretations(long state) { |
31 | return new ModelImpl(this, state, adapters.size()); | 32 | return new ModelImpl(this, state, adapters.size()); |
32 | } | 33 | } |
33 | 34 | ||
34 | @Override | 35 | @Override |
35 | public ModelImpl createModel() { | 36 | public ModelImpl createEmptyModel() { |
36 | var model = createEmptyModel(-1); | 37 | var model = createModelWithoutInterpretations(Model.NO_STATE_ID); |
37 | var interpretations = new HashMap<AnySymbol, VersionedInterpretation<?>>(stores.size()); | 38 | var interpretations = new HashMap<AnySymbol, VersionedInterpretation<?>>(stores.size()); |
38 | for (var entry : this.stores.entrySet()) { | 39 | for (var entry : this.stores.entrySet()) { |
39 | var symbol = entry.getKey(); | 40 | var symbol = entry.getKey(); |
@@ -45,8 +46,8 @@ public class ModelStoreImpl implements ModelStore { | |||
45 | } | 46 | } |
46 | 47 | ||
47 | @Override | 48 | @Override |
48 | public synchronized ModelImpl createModel(long state) { | 49 | public synchronized ModelImpl createModelForState(long state) { |
49 | var model = createEmptyModel(state); | 50 | var model = createModelWithoutInterpretations(state); |
50 | var interpretations = new HashMap<AnySymbol, VersionedInterpretation<?>>(stores.size()); | 51 | var interpretations = new HashMap<AnySymbol, VersionedInterpretation<?>>(stores.size()); |
51 | for (var entry : this.stores.entrySet()) { | 52 | for (var entry : this.stores.entrySet()) { |
52 | var symbol = entry.getKey(); | 53 | var symbol = entry.getKey(); |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java index 1bdb1cdf..6d82f5d7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java | |||
@@ -77,6 +77,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> { | |||
77 | @Override | 77 | @Override |
78 | public T put(Tuple key, T value) { | 78 | public T put(Tuple key, T value) { |
79 | checkKey(key); | 79 | checkKey(key); |
80 | model.markAsChanged(); | ||
80 | var oldValue = map.put(key, value); | 81 | var oldValue = map.put(key, value); |
81 | notifyListeners(key, oldValue, value, false); | 82 | notifyListeners(key, oldValue, value, false); |
82 | return oldValue; | 83 | return oldValue; |
@@ -88,6 +89,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> { | |||
88 | map.putAll(cursor); | 89 | map.putAll(cursor); |
89 | return; | 90 | return; |
90 | } | 91 | } |
92 | model.markAsChanged(); | ||
91 | if (cursor.getDependingMaps().contains(map)) { | 93 | if (cursor.getDependingMaps().contains(map)) { |
92 | List<Tuple> keys = new ArrayList<>(); | 94 | List<Tuple> keys = new ArrayList<>(); |
93 | List<T> values = new ArrayList<>(); | 95 | List<T> values = new ArrayList<>(); |
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java index e7bff1f9..371b5e47 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java | |||
@@ -1,6 +1,7 @@ | |||
1 | package tools.refinery.store.model.tests; | 1 | package tools.refinery.store.model.tests; |
2 | 2 | ||
3 | import org.junit.jupiter.api.Test; | 3 | import org.junit.jupiter.api.Test; |
4 | import tools.refinery.store.model.Model; | ||
4 | import tools.refinery.store.model.ModelStore; | 5 | import tools.refinery.store.model.ModelStore; |
5 | import tools.refinery.store.representation.Symbol; | 6 | import tools.refinery.store.representation.Symbol; |
6 | import tools.refinery.store.tuple.Tuple; | 7 | import tools.refinery.store.tuple.Tuple; |
@@ -30,7 +31,7 @@ class ModelTest { | |||
30 | var friend = new Symbol<>("friend", 2, Boolean.class, false); | 31 | var friend = new Symbol<>("friend", 2, Boolean.class, false); |
31 | 32 | ||
32 | var store = ModelStore.builder().symbols(person, age, friend).build(); | 33 | var store = ModelStore.builder().symbols(person, age, friend).build(); |
33 | var model = store.createModel(); | 34 | var model = store.createEmptyModel(); |
34 | var personInterpretation = model.getInterpretation(person); | 35 | var personInterpretation = model.getInterpretation(person); |
35 | var ageInterpretation = model.getInterpretation(age); | 36 | var ageInterpretation = model.getInterpretation(age); |
36 | var friendInterpretation = model.getInterpretation(friend); | 37 | var friendInterpretation = model.getInterpretation(friend); |
@@ -59,7 +60,7 @@ class ModelTest { | |||
59 | var person = new Symbol<>("Person", 1, Boolean.class, false); | 60 | var person = new Symbol<>("Person", 1, Boolean.class, false); |
60 | 61 | ||
61 | var store = ModelStore.builder().symbols(person).build(); | 62 | var store = ModelStore.builder().symbols(person).build(); |
62 | var model = store.createModel(); | 63 | var model = store.createEmptyModel(); |
63 | var personInterpretation = model.getInterpretation(person); | 64 | var personInterpretation = model.getInterpretation(person); |
64 | 65 | ||
65 | final Tuple tuple3 = Tuple.of(1, 1, 1); | 66 | final Tuple tuple3 = Tuple.of(1, 1, 1); |
@@ -72,7 +73,7 @@ class ModelTest { | |||
72 | var age = new Symbol<>("age", 1, Integer.class, null); | 73 | var age = new Symbol<>("age", 1, Integer.class, null); |
73 | 74 | ||
74 | var store = ModelStore.builder().symbols(age).build(); | 75 | var store = ModelStore.builder().symbols(age).build(); |
75 | var model = store.createModel(); | 76 | var model = store.createEmptyModel(); |
76 | var ageInterpretation = model.getInterpretation(age); | 77 | var ageInterpretation = model.getInterpretation(age); |
77 | 78 | ||
78 | ageInterpretation.put(Tuple.of(1), null); // valid | 79 | ageInterpretation.put(Tuple.of(1), null); // valid |
@@ -88,7 +89,7 @@ class ModelTest { | |||
88 | var friend = new Symbol<>("friend", 2, Boolean.class, false); | 89 | var friend = new Symbol<>("friend", 2, Boolean.class, false); |
89 | 90 | ||
90 | var store = ModelStore.builder().symbols(person, age, friend).build(); | 91 | var store = ModelStore.builder().symbols(person, age, friend).build(); |
91 | var model = store.createModel(); | 92 | var model = store.createEmptyModel(); |
92 | var personInterpretation = model.getInterpretation(person); | 93 | var personInterpretation = model.getInterpretation(person); |
93 | var ageInterpretation = model.getInterpretation(age); | 94 | var ageInterpretation = model.getInterpretation(age); |
94 | var friendInterpretation = model.getInterpretation(friend); | 95 | var friendInterpretation = model.getInterpretation(friend); |
@@ -116,7 +117,7 @@ class ModelTest { | |||
116 | var friend = new Symbol<>("friend", 2, Boolean.class, false); | 117 | var friend = new Symbol<>("friend", 2, Boolean.class, false); |
117 | 118 | ||
118 | var store = ModelStore.builder().symbols(person, friend).build(); | 119 | var store = ModelStore.builder().symbols(person, friend).build(); |
119 | var model = store.createModel(); | 120 | var model = store.createEmptyModel(); |
120 | var personInterpretation = model.getInterpretation(person); | 121 | var personInterpretation = model.getInterpretation(person); |
121 | var friendInterpretation = model.getInterpretation(friend); | 122 | var friendInterpretation = model.getInterpretation(friend); |
122 | 123 | ||
@@ -124,25 +125,45 @@ class ModelTest { | |||
124 | personInterpretation.put(Tuple.of(1), true); | 125 | personInterpretation.put(Tuple.of(1), true); |
125 | friendInterpretation.put(Tuple.of(0, 1), true); | 126 | friendInterpretation.put(Tuple.of(0, 1), true); |
126 | friendInterpretation.put(Tuple.of(1, 0), true); | 127 | friendInterpretation.put(Tuple.of(1, 0), true); |
128 | |||
129 | assertTrue(model.hasUncommittedChanges()); | ||
130 | assertEquals(Model.NO_STATE_ID, model.getState()); | ||
131 | |||
127 | long state1 = model.commit(); | 132 | long state1 = model.commit(); |
128 | 133 | ||
134 | assertFalse(model.hasUncommittedChanges()); | ||
135 | assertEquals(state1, model.getState()); | ||
136 | |||
129 | assertFalse(personInterpretation.get(Tuple.of(2))); | 137 | assertFalse(personInterpretation.get(Tuple.of(2))); |
130 | assertFalse(friendInterpretation.get(Tuple.of(0, 2))); | 138 | assertFalse(friendInterpretation.get(Tuple.of(0, 2))); |
131 | 139 | ||
132 | personInterpretation.put(Tuple.of(2), true); | 140 | personInterpretation.put(Tuple.of(2), true); |
133 | friendInterpretation.put(Tuple.of(0, 2), true); | 141 | friendInterpretation.put(Tuple.of(0, 2), true); |
142 | |||
143 | assertTrue(model.hasUncommittedChanges()); | ||
144 | assertEquals(state1, model.getState()); | ||
145 | |||
134 | long state2 = model.commit(); | 146 | long state2 = model.commit(); |
135 | 147 | ||
148 | assertFalse(model.hasUncommittedChanges()); | ||
149 | assertEquals(state2, model.getState()); | ||
150 | |||
136 | assertTrue(personInterpretation.get(Tuple.of(2))); | 151 | assertTrue(personInterpretation.get(Tuple.of(2))); |
137 | assertTrue(friendInterpretation.get(Tuple.of(0, 2))); | 152 | assertTrue(friendInterpretation.get(Tuple.of(0, 2))); |
138 | 153 | ||
139 | model.restore(state1); | 154 | model.restore(state1); |
140 | 155 | ||
156 | assertFalse(model.hasUncommittedChanges()); | ||
157 | assertEquals(state1, model.getState()); | ||
158 | |||
141 | assertFalse(personInterpretation.get(Tuple.of(2))); | 159 | assertFalse(personInterpretation.get(Tuple.of(2))); |
142 | assertFalse(friendInterpretation.get(Tuple.of(0, 2))); | 160 | assertFalse(friendInterpretation.get(Tuple.of(0, 2))); |
143 | 161 | ||
144 | model.restore(state2); | 162 | model.restore(state2); |
145 | 163 | ||
164 | assertFalse(model.hasUncommittedChanges()); | ||
165 | assertEquals(state2, model.getState()); | ||
166 | |||
146 | assertTrue(personInterpretation.get(Tuple.of(2))); | 167 | assertTrue(personInterpretation.get(Tuple.of(2))); |
147 | assertTrue(friendInterpretation.get(Tuple.of(0, 2))); | 168 | assertTrue(friendInterpretation.get(Tuple.of(0, 2))); |
148 | } | 169 | } |