--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2013 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ * Semantum Oy - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.db.layer0;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.VariantBinding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;\r
+import org.simantics.databoard.binding.mutable.Variant;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.simulator.variable.NodeManager;\r
+import org.simantics.simulator.variable.Realm;\r
+import org.simantics.simulator.variable.exceptions.NoSuchNodeException;\r
+import org.simantics.simulator.variable.exceptions.NodeManagerException;\r
+import org.simantics.simulator.variable.exceptions.NotInRealmException;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.procedure.TObjectProcedure;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+/**\r
+ * StandardNodeManager gives default implementations to some methods\r
+ * of NodeManager.\r
+ * \r
+ * @author Antti Villberg\r
+ */\r
+public abstract class StandardNodeManager<Node,Engine extends StandardEngine<Node>> implements NodeManager<Node> {\r
+ \r
+ final private Node root;\r
+ final private StandardRealm<Node,Engine> realm;\r
+\r
+ final static Binding NO_BINDING = new VariantBinding() {\r
+\r
+ @Override\r
+ public Object getContent(Object variant, Binding contentBinding) throws BindingException {\r
+ throw new Error();\r
+ }\r
+\r
+ @Override\r
+ public Object getContent(Object variant) throws BindingException {\r
+ throw new Error();\r
+ }\r
+\r
+ @Override\r
+ public Datatype getContentType(Object variant) throws BindingException {\r
+ throw new Error();\r
+ }\r
+\r
+ @Override\r
+ public Binding getContentBinding(Object variant) throws BindingException {\r
+ throw new Error();\r
+ }\r
+\r
+ @Override\r
+ public Object create(Binding contentBinding, Object content) throws BindingException {\r
+ throw new Error();\r
+ }\r
+\r
+ @Override\r
+ public void setContent(Object variant, Binding contentBinding, Object content) throws BindingException {\r
+ throw new Error();\r
+ }\r
+\r
+ @Override\r
+ public boolean isInstance(Object obj) {\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public void assertInstaceIsValid(Object obj, Set<Object> validInstances) throws BindingException {\r
+ throw new Error();\r
+ }\r
+ \r
+ @Override\r
+ public int compare(Object o1, Object o2) throws org.simantics.databoard.binding.error.RuntimeBindingException {\r
+ if(o1 == null) {\r
+ if(o2 == null) {\r
+ return 0;\r
+ } else {\r
+ return - System.identityHashCode(o2);\r
+ }\r
+ } else {\r
+ if(o2 == null) {\r
+ return System.identityHashCode(o1);\r
+ } else {\r
+ if(o1.equals(o2)) return 0;\r
+ return System.identityHashCode(o1) - System.identityHashCode(o2); \r
+ }\r
+ }\r
+ }\r
+\r
+ };\r
+ \r
+ THashMap<Node, Object> valueCache = new THashMap<Node, Object>(); \r
+ protected THashMap<Node, THashSet<Runnable>> listeners = new THashMap<Node, 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 StandardNodeManager(StandardRealm<Node,Engine> realm, Node root) {\r
+ this.realm = realm;\r
+ this.root = root;\r
+ }\r
+ \r
+ @Override\r
+ public List<String> getChildNames(Node node) throws NodeManagerException {\r
+ List<Node> children = getChildren(node);\r
+ ArrayList<String> names = new ArrayList<String>(children.size());\r
+ for(Node child : children)\r
+ names.add(getName(child));\r
+ return names;\r
+ }\r
+ \r
+ @Override\r
+ public List<String> getPropertyNames(Node node) throws NodeManagerException {\r
+ List<Node> properties = getProperties(node);\r
+ ArrayList<String> names = new ArrayList<String>(properties.size());\r
+ for(Node property : properties)\r
+ names.add(getName(property));\r
+ return names;\r
+ }\r
+ \r
+ @Override\r
+ public Object getValue(Node node, String propertyName, Binding binding)\r
+ throws NodeManagerException, BindingException {\r
+ Node property = getProperty(node, propertyName);\r
+ if(property == null)\r
+ throw new NoSuchNodeException("Didn't find a property " + propertyName);\r
+ return getValue(property, binding);\r
+ }\r
+ \r
+ @Override\r
+ public void setValue(Node node, String propertyName, Object value,\r
+ Binding binding) throws NodeManagerException, BindingException {\r
+ Node property = getProperty(node, propertyName);\r
+ if(property == null)\r
+ throw new NoSuchNodeException("Didn't find a property " + propertyName);\r
+ setValue(property, value, binding);\r
+ }\r
+ \r
+ @Override\r
+ public Variant getValue(Node node) throws NodeManagerException {\r
+ Object value = getEngineValueOrCached(node);\r
+ if (value instanceof Variant)\r
+ return (Variant) value;\r
+ try {\r
+ Binding binding = Bindings.getBinding(value.getClass());\r
+ return new Variant(binding, value);\r
+ } catch (BindingConstructionException e) {\r
+ e.printStackTrace();\r
+ return null;\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public Variant getValue(Node node, String propertyName)\r
+ throws NodeManagerException {\r
+ Node property = getProperty(node, propertyName);\r
+ if(property == null)\r
+ throw new NoSuchNodeException("Didn't find a property " + propertyName);\r
+ return getValue(property);\r
+ }\r
+ \r
+ @Override\r
+ public String getPropertyURI(Node parent, Node property) {\r
+ return null;\r
+ }\r
+ \r
+ @Override\r
+ public Realm getRealm() {\r
+ return realm;\r
+ }\r
+ \r
+ public StandardRealm<Node, Engine> getStandardRealm() {\r
+ return realm;\r
+ }\r
+ \r
+ protected String getRealmId() {\r
+ return realm.id;\r
+ }\r
+ \r
+ public Node getRoot() {\r
+ return root;\r
+ }\r
+ \r
+ protected boolean isRoot(Node node) {\r
+ return root.equals(node);\r
+ }\r
+\r
+ @Override\r
+ public void addNodeListener(Node 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
+ getRealm().asyncExec(listener);\r
+ }\r
+\r
+ @Override\r
+ public void removeNodeListener(Node 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
+ protected Object getEngineValueOrCached(Node node) throws NodeManagerException {\r
+ Object value = valueCache.get(node);\r
+ if(value == null) {\r
+ value = realm.getEngine().getValue(node);\r
+ valueCache.put(node, value);\r
+ }\r
+ return value;\r
+ }\r
+ \r
+\r
+ @Override\r
+ public Object getValue(Node node, Binding binding) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ return getEngineValueOrCached(node);\r
+ }\r
+\r
+ protected void checkThreadAccess() throws NodeManagerException {\r
+ if(Thread.currentThread() != realm.getThread())\r
+ throw new NotInRealmException();\r
+ }\r
+ \r
+ protected Datatype getDatatypeForValue(Object value) throws DatabaseException {\r
+ Binding binding = Bindings.getBindingUnchecked(value.getClass());\r
+ if(binding == null) return null;\r
+ else return binding.type();\r
+ }\r
+ \r
+ @Override\r
+ public void setValue(Node node, Object value, Binding binding)\r
+ throws NodeManagerException {\r
+ checkThreadAccess();\r
+ valueCache.put(node, value);\r
+ realm.getEngine().setValue(node, value);\r
+ realm.nodeManager.valueCache.put(node, value);\r
+ refreshVariables();\r
+ }\r
+ \r
+ @Override\r
+ public String getName(Node node) {\r
+ if(isRoot(node)) {\r
+ String id = getRealmId();\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 realm.getEngine().getName(node);\r
+ }\r
+ }\r
+ \r
+\r
+ @Override\r
+ public Node getNode(String path) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ throw new UnsupportedOperationException();\r
+ }\r
+ \r
+ @Override\r
+ public Node getChild(Node node, String name) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ Map<String,Node> map = realm.getEngine().getChildren(node);\r
+ return map.get(name);\r
+ }\r
+\r
+ @Override\r
+ public Node getProperty(Node node, String name) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ Map<String,Node> map = realm.getEngine().getProperties(node);\r
+ return map.get(name);\r
+ }\r
+\r
+ @Override\r
+ public List<Node> getChildren(Node node) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ return new ArrayList<Node>(realm.getEngine().getChildren(node).values());\r
+ }\r
+\r
+ @Override\r
+ public List<Node> getProperties(Node node) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ return new ArrayList<Node>(realm.getEngine().getProperties(node).values());\r
+ }\r
+\r
+ @Override\r
+ public Datatype getDatatype(Node node) throws NodeManagerException {\r
+ checkThreadAccess();\r
+ try {\r
+ Datatype type = getDatatypeForValue(getEngineValueOrCached(node));\r
+ return type;\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ } catch (RuntimeBindingConstructionException e) {\r
+ // There is no datatype for all values\r
+ }\r
+ return null;\r
+ }\r
+ \r
+}\r