package net.mograsim.logic.model.model.components.atomic;
+import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import net.haspamelodica.swt.helper.gcs.GeneralGC;
import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer;
import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer.PinNamesParams;
import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer.PinNamesParams.Position;
+import net.mograsim.logic.model.util.ObservableAtomicReference;
public abstract class SimpleRectangularHardcodedModelComponent extends ModelComponent
{
private final CenteredTextSymbolRenderer centerTextRenderer;
private final PinNamesSymbolRenderer pinNamesRenderer;
- private AtomicReference<Object> state;
+ private ObservableAtomicReference<Object> state;
private Runnable recalculate;
+ private final Map<String, Map<Consumer<Object>, Consumer<ObservableAtomicReference<Object>>>> stateObsPerHLSListenerPerStateID;
+
// creation and destruction
public SimpleRectangularHardcodedModelComponent(LogicModelModifiable model, String id, String name, String centerText)
pinNamesParams.pinLabelMargin = pinNamesMargin;
this.pinNamesRenderer = new PinNamesSymbolRenderer(this, pinNamesParams);
addPinRemovedListener(this::pinRemoved);
+
+ this.stateObsPerHLSListenerPerStateID = new HashMap<>();
+
setHighLevelStateHandler(new HighLevelStateHandler()
{
+
@Override
- public String getIDForSerializing(IdentifyParams idParams)
+ public Object get(String stateID)
{
- return null;// we don't need to serialize this; it's implicit since we are a SimpleRectangularHardcodedModelComponent
+ return getHighLevelState(state.get(), stateID);
}
@Override
- public Object getParamsForSerializing(IdentifyParams idParams)
+ public void set(String stateID, Object newState)
{
- return null;
+ state.updateAndGet(s -> SimpleRectangularHardcodedModelComponent.this.setHighLevelState(s, stateID, newState));
+ recalculate.run();
}
@Override
- public Object getHighLevelState(String stateID)
+ public void addListener(String stateID, Consumer<Object> stateChanged)
{
- return SimpleRectangularHardcodedModelComponent.this.getHighLevelState(state.get(), stateID);
+ addHighLevelStateListener(state.get(), stateID, stateChanged);
}
@Override
- public void setHighLevelState(String stateID, Object newState)
+ public void removeListener(String stateID, Consumer<Object> stateChanged)
{
- state.updateAndGet(s -> SimpleRectangularHardcodedModelComponent.this.setHighLevelState(s, stateID, newState));
- recalculate.run();
+ removeHighLevelStateListener(state.get(), stateID, stateChanged);
+ }
+
+ @Override
+ public String getIDForSerializing(IdentifyParams idParams)
+ {
+ return null;// we don't need to serialize this; it's implicit since we are a SimpleRectangularHardcodedModelComponent
+ }
+
+ @Override
+ public Object getParamsForSerializing(IdentifyParams idParams)
+ {
+ return null;
}
});
throw new IllegalArgumentException("No high level state with ID " + stateID);
}
+ protected void addHighLevelStateListener(Object state, String stateID, Consumer<Object> stateChanged)
+ {
+ AtomicReference<Object> lastHLSRef = new AtomicReference<>(getHighLevelState(state, stateID));
+ Consumer<ObservableAtomicReference<Object>> refObs = r ->
+ {
+ Object newHLS = getHighLevelState(stateID);
+ if (!Objects.equals(lastHLSRef.getAndSet(newHLS), newHLS))
+ stateChanged.accept(newHLS);
+ };
+ stateObsPerHLSListenerPerStateID.computeIfAbsent(stateID, s -> new HashMap<>()).put(stateChanged, refObs);
+ this.state.addObserver(refObs);
+ }
+
+ protected void removeHighLevelStateListener(Object state, String stateID, Consumer<Object> stateChanged)
+ {
+ getHighLevelState(state, stateID);// if this throws, we know there is no HLS with this name
+ var stateObsPerHLSListener = stateObsPerHLSListenerPerStateID.get(stateID);
+ if (stateObsPerHLSListener == null)
+ return;
+ Consumer<ObservableAtomicReference<Object>> refObs = stateObsPerHLSListener.remove(stateChanged);
+ this.state.removeObserver(refObs);
+ }
+
// logic
public abstract Object recalculate(Object lastState, Map<String, ReadEnd> readEnds, Map<String, ReadWriteEnd> readWriteEnds);
// core model binding
- public void setCoreModelBindingAndResetState(AtomicReference<Object> state, Runnable recalculate)
+ public void setCoreModelBindingAndResetState(ObservableAtomicReference<Object> state, Runnable recalculate)
{
this.state = state;
this.recalculate = recalculate;