--- /dev/null
+package org.simantics.document.server.state;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.error.BindingConstructionException;
+import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
+import org.simantics.databoard.serialization.SerializerConstructionException;
+import org.simantics.db.layer0.variable.NodeSupport;
+import org.simantics.simulator.toolkit.StandardNodeManager;
+import org.simantics.simulator.toolkit.StandardRealm;
+import org.simantics.simulator.variable.exceptions.NodeManagerException;
+import org.slf4j.LoggerFactory;
+
+public class StateNodeManager extends StandardNodeManager<StateNode, StateNodeManagerSupport> {
+
+ private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(StateNodeManager.class);
+
+ private NodeSupport<StateNode> support;
+
+ public StateNodeManager(StandardRealm<StateNode, StateNodeManagerSupport> realm, StateNode root) {
+ super(realm, root);
+ }
+
+ public void registerSupport(NodeSupport<StateNode> support) {
+ this.support = support;
+ }
+
+ @Override
+ public Set<String> getClassifications(StateNode node) throws NodeManagerException {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void refreshVariable(StateNode node) {
+ super.refreshVariable(node);
+ support.valueCache.clearExpired();
+ support.structureCache.clearExpired();
+ }
+
+ public void setState(String key, Object value) {
+ try {
+ getRealm().syncExec(() -> {
+ try {
+ StateRootNode rootNode = (StateRootNode) getRoot();
+ StatePropertyNode propertyNode = rootNode.getProperty(key);
+ if (propertyNode == null) {
+ setValue(rootNode.createProperty(key), value, Bindings.OBJECT);
+ refreshVariable(rootNode);
+ } else {
+ setValue(propertyNode, value, Bindings.OBJECT);
+ }
+ } catch (NodeManagerException e) {
+ LOGGER.error("Failed to set state.", e);
+ }
+ });
+ } catch (InterruptedException e) {
+ LOGGER.error("Setting state was interrupted.", e);
+ }
+ }
+
+ public byte[] serialize() {
+ final byte [][] result = new byte[1][];
+ try {
+ getRealm().syncExec(() -> {
+ State state = new State();
+ StateRootNode root = (StateRootNode) getRoot();
+ for (StateNode node : root.getProperties().values()) {
+ StatePropertyNode property = (StatePropertyNode)node;
+ try {
+ Binding binding = Bindings.getInstanceBinding(property.getValue());
+ if (binding != null) {
+ state.properties.put(property.getName(), new Variant(binding, property.getValue()));
+ }
+ } catch (BindingConstructionException e) {
+ // skip
+ }
+ }
+
+ try {
+ result[0] = Bindings.getSerializerUnchecked(State.BINDING).serialize(state);
+ } catch (RuntimeSerializerConstructionException | IOException e) {
+ }
+ });
+ } catch (InterruptedException e) {
+ LOGGER.error("Serializing state was interrupted.", e);
+ }
+ return result[0];
+ }
+
+ public void deserialize(byte[] bytes) {
+ try {
+ getRealm().syncExec(() -> {
+ StateRootNode rootNode = (StateRootNode) getRoot();
+ rootNode.clear();
+ try {
+ State state = (State) Bindings.getSerializer(State.BINDING).deserialize(bytes);
+ for (Map.Entry<String, Variant> entry : state.properties.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue().getValue();
+ try {
+ setValue(rootNode.createProperty(key), value, Bindings.OBJECT);
+ } catch (NodeManagerException e) {
+ LOGGER.error("Failed to deserialize state.", e);
+ }
+ }
+ refreshVariable(rootNode);
+ } catch (IOException e1) {
+ LOGGER.error("Failed to deserialize state.", e1);
+ } catch (SerializerConstructionException e1) {
+ LOGGER.error("Failed to deserialize state.", e1);
+ }
+ });
+ } catch (InterruptedException e) {
+ LOGGER.error("Deserializing state was interrupted.", e);
+ }
+ }
+
+ public void clearState() {
+ try {
+ getRealm().syncExec(() -> {
+ StateRootNode rootNode = (StateRootNode) getRoot();
+ rootNode.clear();
+ refreshVariable(rootNode);
+ });
+ } catch (InterruptedException e) {
+ LOGGER.error("Clearing state was interrupted.", e);
+ }
+ }
+
+ public void removeState(String key) {
+ try {
+ getRealm().syncExec(() -> {
+ StateRootNode rootNode = (StateRootNode) getRoot();
+ if (rootNode.removeProperty(key)) {
+ refreshVariable(rootNode);
+ }
+ });
+ } catch (InterruptedException e) {
+ LOGGER.error("Removing state was interrupted.", e);
+ }
+ }
+
+}