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