--- /dev/null
+package org.simantics.modeling.scl;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.variable.NodeSupport;\r
+import org.simantics.scl.compiler.types.Type;\r
+import org.simantics.simulator.variable.Realm;\r
+import org.simantics.simulator.variable.exceptions.NodeManagerException;\r
+import org.simantics.simulator.variable.exceptions.NotInRealmException;\r
+import org.simantics.simulator.variable.impl.AbstractNodeManager;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.procedure.TObjectProcedure;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+public class SCLNodeManager extends AbstractNodeManager<String> {\r
+\r
+ public static final String ROOT = "@";\r
+ \r
+ SCLRealm realm;\r
+ THashMap<String, Object> valueCache = new THashMap<String, Object>(); \r
+ THashMap<String, THashSet<Runnable>> listeners = new THashMap<String, THashSet<Runnable>>(); \r
+ \r
+ AtomicBoolean fireNodeListenersScheduled = new AtomicBoolean(false);\r
+ Runnable fireNodeListeners = new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ fireNodeListenersScheduled.set(false);\r
+ final TObjectProcedure<Runnable> procedure = new TObjectProcedure<Runnable>() {\r
+ @Override\r
+ public boolean execute(Runnable object) {\r
+ object.run();\r
+ return true;\r
+ }\r
+ };\r
+ synchronized(listeners) {\r
+ listeners.forEachValue(new TObjectProcedure<THashSet<Runnable>>() {\r
+ @Override\r
+ public boolean execute(THashSet<Runnable> object) {\r
+ object.forEach(procedure);\r
+ return true;\r
+ }\r
+ });\r
+ }\r
+ }\r
+ };\r
+ \r
+ Runnable clearValueCache = new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ valueCache.clear(); \r
+ }\r
+ };\r
+ \r
+ public SCLNodeManager(SCLRealm realm) {\r
+ super();\r
+ this.realm = realm;\r
+ }\r
+\r
+ @Override\r
+ public Realm getRealm() {\r
+ return realm;\r
+ }\r
+\r
+ public String getRoot() {\r
+ return ROOT;\r
+ }\r
+ \r
+ @Override\r
+ public String getName(String node) {\r
+ if(ROOT.equals(node)) {\r
+ String id = realm.id;\r
+ int lastSlash = id.lastIndexOf("/");\r
+ if(lastSlash == -1) throw new IllegalStateException("Invalid realm id " + id);\r
+ String name = id.substring(lastSlash+1); \r
+ return name;\r
+ } else\r
+ return node;\r
+ }\r
+\r
+ @Override\r
+ public void addNodeListener(String node, Runnable listener) {\r
+ synchronized(listeners) {\r
+ THashSet<Runnable> l = listeners.get(node);\r
+ if(l == null) {\r
+ l = new THashSet<Runnable>();\r
+ listeners.put(node, l);\r
+ }\r
+ l.add(listener);\r
+ }\r
+ realm.asyncExec(listener);\r
+ }\r
+\r
+ @Override\r
+ public void removeNodeListener(String node, Runnable listener) {\r
+ synchronized(listeners) {\r
+ THashSet<Runnable> l = listeners.get(node);\r
+ if(l != null) {\r
+ l.remove(listener);\r
+ if(l.isEmpty())\r
+ listeners.remove(node);\r
+ }\r
+ }\r
+ }\r
+ \r
+ public void fireNodeListeners() {\r
+ if(!fireNodeListenersScheduled.getAndSet(true))\r
+ realm.asyncExec(fireNodeListeners);\r
+ }\r
+ \r
+ public void fireNodeListenersSync() {\r
+ try {\r
+ realm.syncExec(fireNodeListeners);\r
+ } catch (InterruptedException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ public void refreshVariables() {\r
+ realm.asyncExec(clearValueCache);\r
+ fireNodeListeners();\r
+ }\r
+\r
+ public void refreshVariablesSync() {\r
+ try {\r
+ realm.syncExec(clearValueCache);\r
+ } catch (InterruptedException e) {\r
+ e.printStackTrace();\r
+ }\r
+ fireNodeListenersSync();\r
+ }\r
+\r
+ @Override\r
+ public String getNode(String path) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public String getChild(String node, String name)\r
+ throws NodeManagerException {\r
+ checkThreadAccess();\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public String getProperty(String node, String name)\r
+ throws NodeManagerException {\r
+ checkThreadAccess();\r
+ if(node.equals(ROOT))\r
+ return name;\r
+ else\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public List<String> getChildren(String node) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ return Collections.emptyList();\r
+ }\r
+\r
+ @Override\r
+ public List<String> getProperties(String node) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ if(!node.equals(ROOT))\r
+ return Collections.emptyList();\r
+ \r
+ Set<String> variables = realm.getConnection().getVariables();\r
+ return new ArrayList<String>(variables);\r
+ \r
+ }\r
+\r
+ @Override\r
+ public Datatype getDatatype(String node) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ try {\r
+ Datatype type = getDatatypeForValue(getSCLValue(node));\r
+ return type;\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public Object getValue(String node, Binding binding) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ return getSCLValue(node);\r
+ }\r
+\r
+ \r
+ private Type getType(String name) {\r
+ return realm.getConnection().getVariableType(name);\r
+ }\r
+\r
+ private Datatype getDatatypeForValue(Object value) throws DatabaseException {\r
+ if(value instanceof Double) return Datatypes.DOUBLE;\r
+ if(value instanceof Float) return Datatypes.FLOAT;\r
+ if(value instanceof Integer) return Datatypes.INTEGER;\r
+ if(value instanceof Long) return Datatypes.LONG;\r
+ if(value instanceof String) return Datatypes.STRING;\r
+ if(value instanceof Boolean) return Datatypes.BOOLEAN;\r
+ \r
+ if (value instanceof List) return null;\r
+ \r
+ if (value instanceof Object) return null;\r
+ \r
+ if (value == null) return null;\r
+ \r
+ else throw new DatabaseException("No Datatype for value " + value);\r
+ }\r
+\r
+ @Override\r
+ public void setValue(String node, Object value, Binding binding)\r
+ throws NodeManagerException {\r
+ checkThreadAccess();\r
+ valueCache.put(node, value);\r
+ realm.getConnection().setVariable(node, getType(node), value);\r
+ realm.nodeManager.valueCache.put(node, value);\r
+ refreshVariables();\r
+ }\r
+\r
+ public void setValue(String node, Type type, Object value)\r
+ throws NodeManagerException {\r
+ \r
+ checkThreadAccess();\r
+ valueCache.put(node, value);\r
+ realm.getConnection().setVariable(node, type, value);\r
+ \r
+ NodeSupport support = SCLSessionManager.getOrCreateNodeSupport(realm.getId());\r
+ support.structureCache.put(ROOT, null);\r
+ support.valueCache.put(node, null);\r
+ \r
+ realm.nodeManager.valueCache.put(node, value);\r
+ realm.nodeManager.\r
+ refreshVariables();\r
+ }\r
+ \r
+ static final Set<String> COMPONENT_CLASS = Collections.singleton(StructuralResource2.URIs.Component);\r
+ \r
+ @Override\r
+ public Set<String> getClassifications(String node) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ if(node.equals(ROOT))\r
+ return COMPONENT_CLASS;\r
+ else\r
+ return Collections.emptySet();\r
+ }\r
+\r
+ private Object getSCLValue(String node) throws NodeManagerException {\r
+ Object value = valueCache.get(node);\r
+ if(value == null) {\r
+ value = realm.getConnection().getVariableValue(node);\r
+ valueCache.put(node, value);\r
+ }\r
+ return value;\r
+ }\r
+ \r
+ private void checkThreadAccess() throws NodeManagerException {\r
+ if(Thread.currentThread() != realm.getThread())\r
+ throw new NotInRealmException();\r
+ }\r
+ \r
+ @Override\r
+ public String getPropertyURI(String parent, String property) {\r
+ return "http://www.simantics.org/Modeling-1.2/CommandSession/hasValue";\r
+ }\r
+}\r