]> gerrit.simantics Code Review - simantics/python.git/blobdiff - org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNodeManager.java
Added support for dynamically typed data access and a Variable interface.
[simantics/python.git] / org.simantics.pythonlink / src / org / simantics / pythonlink / variable / PythonNodeManager.java
diff --git a/org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNodeManager.java b/org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNodeManager.java
new file mode 100644 (file)
index 0000000..b88ebfa
--- /dev/null
@@ -0,0 +1,281 @@
+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