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