From f062947f68e79dc866fc0d8d1f9b8874fd00b51e Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 2 Feb 2023 17:20:35 +0100 Subject: feat: track uncommitted Model changes --- .../java/tools/refinery/store/model/Model.java | 4 +++ .../tools/refinery/store/model/ModelStore.java | 4 +-- .../refinery/store/model/internal/ModelImpl.java | 33 ++++++++++++++++++---- .../store/model/internal/ModelStoreImpl.java | 11 ++++---- .../model/internal/VersionedInterpretation.java | 2 ++ .../refinery/store/model/tests/ModelTest.java | 31 ++++++++++++++++---- 6 files changed, 67 insertions(+), 18 deletions(-) (limited to 'subprojects/store/src') 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; import java.util.Optional; public interface Model extends Versioned { + long NO_STATE_ID = -1; + ModelStore getStore(); long getState(); + boolean hasUncommittedChanges(); + default AnyInterpretation getInterpretation(AnySymbol symbol) { return getInterpretation((Symbol) symbol); } 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; public interface ModelStore { Collection getSymbols(); - Model createModel(); + Model createEmptyModel(); - Model createModel(long state); + Model createModelForState(long state); Set getStates(); 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 { private Map> interpretations; private final AdapterList adapters; private final List listeners = new ArrayList<>(); + private boolean uncommittedChanges; private ModelAction pendingAction = ModelAction.NONE; - private long restoringToState = -1; + private long restoringToState = NO_STATE_ID; ModelImpl(ModelStore store, long state, int adapterCount) { this.store = store; @@ -61,9 +62,29 @@ public class ModelImpl implements Model { return new ModelDiffCursor(diffCursors); } + private void setState(long state) { + this.state = state; + uncommittedChanges = false; + } + + void markAsChanged() { + if (!uncommittedChanges) { + uncommittedChanges = true; + } + } + + @Override + public boolean hasUncommittedChanges() { + return uncommittedChanges; + } + + private boolean hasPendingAction() { + return pendingAction != ModelAction.NONE || restoringToState != NO_STATE_ID; + } + @Override public long commit() { - if (pendingAction != ModelAction.NONE) { + if (hasPendingAction()) { throw pendingActionError("commit"); } pendingAction = ModelAction.COMMIT; @@ -88,7 +109,7 @@ public class ModelImpl implements Model { versionSet = true; } } - state = version; + setState(version); while (i < listenerCount) { listeners.get(i).afterCommit(); i++; @@ -101,7 +122,7 @@ public class ModelImpl implements Model { @Override public void restore(long version) { - if (pendingAction != ModelAction.NONE) { + if (hasPendingAction()) { throw pendingActionError("restore to %d".formatted(version)); } if (!store.getStates().contains(version)) { @@ -119,14 +140,14 @@ public class ModelImpl implements Model { for (var interpretation : interpretations.values()) { interpretation.restore(version); } - state = version; + setState(version); while (i < listenerCount) { listeners.get(i).afterRestore(); i++; } } finally { pendingAction = ModelAction.NONE; - restoringToState = -1; + restoringToState = NO_STATE_ID; } } 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; import tools.refinery.store.adapter.ModelStoreAdapter; import tools.refinery.store.map.DiffCursor; import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelDiffCursor; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.AnySymbol; @@ -27,13 +28,13 @@ public class ModelStoreImpl implements ModelStore { return Collections.unmodifiableCollection(stores.keySet()); } - private ModelImpl createEmptyModel(long state) { + private ModelImpl createModelWithoutInterpretations(long state) { return new ModelImpl(this, state, adapters.size()); } @Override - public ModelImpl createModel() { - var model = createEmptyModel(-1); + public ModelImpl createEmptyModel() { + var model = createModelWithoutInterpretations(Model.NO_STATE_ID); var interpretations = new HashMap>(stores.size()); for (var entry : this.stores.entrySet()) { var symbol = entry.getKey(); @@ -45,8 +46,8 @@ public class ModelStoreImpl implements ModelStore { } @Override - public synchronized ModelImpl createModel(long state) { - var model = createEmptyModel(state); + public synchronized ModelImpl createModelForState(long state) { + var model = createModelWithoutInterpretations(state); var interpretations = new HashMap>(stores.size()); for (var entry : this.stores.entrySet()) { 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 implements Interpretation { @Override public T put(Tuple key, T value) { checkKey(key); + model.markAsChanged(); var oldValue = map.put(key, value); notifyListeners(key, oldValue, value, false); return oldValue; @@ -88,6 +89,7 @@ public class VersionedInterpretation implements Interpretation { map.putAll(cursor); return; } + model.markAsChanged(); if (cursor.getDependingMaps().contains(map)) { List keys = new ArrayList<>(); List 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 @@ package tools.refinery.store.model.tests; import org.junit.jupiter.api.Test; +import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; @@ -30,7 +31,7 @@ class ModelTest { var friend = new Symbol<>("friend", 2, Boolean.class, false); var store = ModelStore.builder().symbols(person, age, friend).build(); - var model = store.createModel(); + var model = store.createEmptyModel(); var personInterpretation = model.getInterpretation(person); var ageInterpretation = model.getInterpretation(age); var friendInterpretation = model.getInterpretation(friend); @@ -59,7 +60,7 @@ class ModelTest { var person = new Symbol<>("Person", 1, Boolean.class, false); var store = ModelStore.builder().symbols(person).build(); - var model = store.createModel(); + var model = store.createEmptyModel(); var personInterpretation = model.getInterpretation(person); final Tuple tuple3 = Tuple.of(1, 1, 1); @@ -72,7 +73,7 @@ class ModelTest { var age = new Symbol<>("age", 1, Integer.class, null); var store = ModelStore.builder().symbols(age).build(); - var model = store.createModel(); + var model = store.createEmptyModel(); var ageInterpretation = model.getInterpretation(age); ageInterpretation.put(Tuple.of(1), null); // valid @@ -88,7 +89,7 @@ class ModelTest { var friend = new Symbol<>("friend", 2, Boolean.class, false); var store = ModelStore.builder().symbols(person, age, friend).build(); - var model = store.createModel(); + var model = store.createEmptyModel(); var personInterpretation = model.getInterpretation(person); var ageInterpretation = model.getInterpretation(age); var friendInterpretation = model.getInterpretation(friend); @@ -116,7 +117,7 @@ class ModelTest { var friend = new Symbol<>("friend", 2, Boolean.class, false); var store = ModelStore.builder().symbols(person, friend).build(); - var model = store.createModel(); + var model = store.createEmptyModel(); var personInterpretation = model.getInterpretation(person); var friendInterpretation = model.getInterpretation(friend); @@ -124,25 +125,45 @@ class ModelTest { personInterpretation.put(Tuple.of(1), true); friendInterpretation.put(Tuple.of(0, 1), true); friendInterpretation.put(Tuple.of(1, 0), true); + + assertTrue(model.hasUncommittedChanges()); + assertEquals(Model.NO_STATE_ID, model.getState()); + long state1 = model.commit(); + assertFalse(model.hasUncommittedChanges()); + assertEquals(state1, model.getState()); + assertFalse(personInterpretation.get(Tuple.of(2))); assertFalse(friendInterpretation.get(Tuple.of(0, 2))); personInterpretation.put(Tuple.of(2), true); friendInterpretation.put(Tuple.of(0, 2), true); + + assertTrue(model.hasUncommittedChanges()); + assertEquals(state1, model.getState()); + long state2 = model.commit(); + assertFalse(model.hasUncommittedChanges()); + assertEquals(state2, model.getState()); + assertTrue(personInterpretation.get(Tuple.of(2))); assertTrue(friendInterpretation.get(Tuple.of(0, 2))); model.restore(state1); + assertFalse(model.hasUncommittedChanges()); + assertEquals(state1, model.getState()); + assertFalse(personInterpretation.get(Tuple.of(2))); assertFalse(friendInterpretation.get(Tuple.of(0, 2))); model.restore(state2); + assertFalse(model.hasUncommittedChanges()); + assertEquals(state2, model.getState()); + assertTrue(personInterpretation.get(Tuple.of(2))); assertTrue(friendInterpretation.get(Tuple.of(0, 2))); } -- cgit v1.2.3-70-g09d2