--- /dev/null
+package org.simantics.pythonlink.variable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.mutable.Variant;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.MapType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.pythonlink.NDArray;\r
+import org.simantics.pythonlink.PythonContext;\r
+import org.simantics.pythonlink.PythonContext.Listener;\r
+import org.simantics.pythonlink.PythonContext.VariableType;\r
+import org.simantics.simulator.variable.Realm;\r
+import org.simantics.simulator.variable.exceptions.NodeManagerException;\r
+import org.simantics.simulator.variable.impl.AbstractNodeManager;\r
+\r
+public class PythonNodeManager extends AbstractNodeManager<PythonNode> {\r
+ PythonContext context;\r
+ \r
+ static Realm realm = new Realm() {\r
+ public void asyncExec(Runnable runnable) {\r
+ runnable.run();\r
+ };\r
+ \r
+ public void syncExec(Runnable runnable) {\r
+ runnable.run();\r
+ };\r
+ };\r
+ \r
+ Map<String, Set<Runnable>> listeners = new HashMap<>();\r
+ Map<String, PythonNode> nodes = new HashMap<>();\r
+ \r
+ PythonNode root = new PythonNode("/");\r
+ \r
+ static Pattern namePattern = Pattern.compile("/?([a-zA-Z_][a-zA-Z_0-9]*)");\r
+ \r
+ static MapType dictionaryType = new MapType(Datatypes.VARIANT, Datatypes.VARIANT);\r
+ static RecordType ndarrayType = (RecordType)Bindings.getBindingUnchecked(NDArray.class).type();\r
+ \r
+ public PythonNodeManager(PythonContext context) {\r
+ super();\r
+ this.context = context;\r
+ \r
+ context.addListener(new Listener() {\r
+ @Override\r
+ public void updated(String variableName) {\r
+ if (variableName != null) {\r
+ Set<Runnable> lis = listeners.get(variableName);\r
+ if (lis != null) {\r
+ for (Runnable r : lis)\r
+ r.run();\r
+ }\r
+ lis = listeners.get("/");\r
+ if (lis != null) {\r
+ for (Runnable r : lis)\r
+ r.run();\r
+ }\r
+ }\r
+ else {\r
+ Set<Runnable> allRunnables = new HashSet<>();\r
+ for (Collection<Runnable> s : listeners.values())\r
+ allRunnables.addAll(s);\r
+ for (Runnable r : allRunnables)\r
+ r.run();\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void closed() {\r
+ nodes.clear();\r
+ Set<Runnable> allRunnables = new HashSet<>();\r
+ for (Collection<Runnable> s : listeners.values())\r
+ allRunnables.addAll(s);\r
+ for (Runnable r : allRunnables)\r
+ r.run();\r
+ }\r
+ });\r
+ }\r
+ \r
+ @Override\r
+ public Realm getRealm() {\r
+ return realm;\r
+ }\r
+\r
+ @Override\r
+ public String getName(PythonNode node) {\r
+ return node.getName();\r
+ }\r
+\r
+ @Override\r
+ public void addNodeListener(PythonNode node, Runnable listener) {\r
+ synchronized(listeners) {\r
+ if (!listeners.containsKey(node.getName())) listeners.put(node.getName(), new HashSet<>());\r
+ listeners.get(node.getName()).add(listener);\r
+ } \r
+ }\r
+\r
+ @Override\r
+ public void removeNodeListener(PythonNode node, Runnable listener) {\r
+ synchronized(listeners) {\r
+ if (!listeners.containsKey(node.getName()))\r
+ listeners.get(node.getName()).remove(listener);\r
+ } \r
+ }\r
+\r
+ @Override\r
+ public PythonNode getNode(String path) throws NodeManagerException {\r
+ if (!context.isOpen())\r
+ return null;\r
+ \r
+ if ("/".equals(path))\r
+ return root; \r
+ \r
+ Matcher match = namePattern.matcher(path);\r
+ if (!match.matches())\r
+ return null;\r
+ \r
+ String name = match.group(1);\r
+ \r
+ if (nodes.containsKey(name))\r
+ return nodes.get(name);\r
+ \r
+ VariableType type = context.getPythonVariableType(name);\r
+ if (type == VariableType.NO_VARIABLE)\r
+ return null;\r
+ \r
+ PythonNode node = new PythonNode(name);\r
+ nodes.put(path, node);\r
+ \r
+ return node;\r
+ }\r
+\r
+ @Override\r
+ public PythonNode getChild(PythonNode node, String name) throws NodeManagerException {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public PythonNode getProperty(PythonNode node, String name) throws NodeManagerException {\r
+ if (node != root || !context.isOpen()) return null;\r
+ \r
+ return getNode(name);\r
+ }\r
+ \r
+ @Override\r
+ public List<String> getChildNames(PythonNode node) throws NodeManagerException {\r
+ return Collections.emptyList();\r
+ }\r
+\r
+ @Override\r
+ public List<PythonNode> getChildren(PythonNode node) throws NodeManagerException {\r
+ return Collections.emptyList();\r
+ }\r
+ \r
+ @Override\r
+ public List<String> getPropertyNames(PythonNode node) throws NodeManagerException {\r
+ if (node != root || !context.isOpen()) return Collections.emptyList(); \r
+ return Arrays.asList(context.getPythonVariableNames());\r
+ }\r
+\r
+ @Override\r
+ public List<PythonNode> getProperties(PythonNode node) throws NodeManagerException {\r
+ if (node != root || !context.isOpen()) return Collections.emptyList();\r
+ \r
+ String[] names = context.getPythonVariableNames();\r
+ List<PythonNode> result = new ArrayList<>(names.length);\r
+ \r
+ for (String name : names) {\r
+ result.add(getNode(name));\r
+ }\r
+ \r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public Datatype getDatatype(PythonNode node) throws NodeManagerException {\r
+ String name = node.getName();\r
+ VariableType type;\r
+ try {\r
+ type = context.getPythonVariableType(name);\r
+ } catch (RuntimeException e) {\r
+ throw new NodeManagerException("Failed to get type of variable " + name, e); \r
+ }\r
+ \r
+ switch (type) {\r
+ case NO_VARIABLE: return Datatypes.VOID;\r
+ case BOOLEAN: return Datatypes.BOOLEAN;\r
+ case LONG: return Datatypes.LONG;\r
+ case FLOAT: return Datatypes.DOUBLE;\r
+ case STRING: return Datatypes.STRING;\r
+ case BYTEARRAY: return Datatypes.BYTE_ARRAY;\r
+ case DICTIONARY: return dictionaryType;\r
+ case NDARRAY: return ndarrayType;\r
+ case SEQUENCE: return Datatypes.VARIANT_ARRAY;\r
+ case UNKNOWN: return Datatypes.VOID;\r
+ default: throw new RuntimeException("Unknown python variable type");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Variant getValue(PythonNode node, String propertyName) throws NodeManagerException {\r
+ if (node == root && context.isOpen())\r
+ return context.getPythonVariantVariable(propertyName);\r
+ \r
+ throw new NodeManagerException("No value available for " + node.getName() + "#" + propertyName);\r
+ }\r
+\r
+ @Override\r
+ public Object getValue(PythonNode node, String propertyName, Binding binding)\r
+ throws NodeManagerException, BindingException {\r
+ Variant value = getValue(node, propertyName);\r
+ \r
+ try {\r
+ return value.getValue(binding);\r
+ } catch (AdaptException e) {\r
+ throw new BindingException(e);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public Variant getValue(PythonNode node) throws NodeManagerException {\r
+ if (node == root || !context.isOpen())\r
+ throw new NodeManagerException("No value available for " + node.getName());\r
+ \r
+ String name = node.getName();\r
+ \r
+ try {\r
+ return context.getPythonVariantVariable(name);\r
+ } catch (RuntimeException e) {\r
+ throw new NodeManagerException("Failed to get value of variable " + name, e);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public Object getValue(PythonNode node, Binding binding) throws NodeManagerException, BindingException {\r
+ Variant value = getValue(node);\r
+ \r
+ try {\r
+ return value.getValue(binding);\r
+ } catch (AdaptException e) {\r
+ throw new BindingException(e);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void setValue(PythonNode node, String propertyName, Object value, Binding binding)\r
+ throws NodeManagerException, BindingException {\r
+ if (node != root || !context.isOpen()) throw new NodeManagerException("No property " + node.getName() + "#" + propertyName);\r
+ \r
+ context.setPythonVariantVariable(propertyName, value, binding); \r
+ }\r
+ \r
+ @Override\r
+ public void setValue(PythonNode node, Object value, Binding binding) throws NodeManagerException, BindingException { \r
+ if (node == root || !context.isOpen()) throw new NodeManagerException("No property " + node.getName());\r
+ String name = node.getName();\r
+ \r
+ context.setPythonVariantVariable(name, value, binding);\r
+ }\r
+ \r
+ @Override\r
+ public Set<String> getClassifications(PythonNode node) throws NodeManagerException {\r
+ return Collections.emptySet();\r
+ }\r
+ \r
+}\r