]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardNodeManager.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / StandardNodeManager.java
1 /*******************************************************************************\r
2  * Copyright (c) 2013 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *     Semantum Oy - initial API and implementation\r
12  *******************************************************************************/\r
13 package org.simantics.db.layer0;\r
14 \r
15 import java.util.ArrayList;\r
16 import java.util.List;\r
17 import java.util.Map;\r
18 import java.util.Set;\r
19 import java.util.concurrent.atomic.AtomicBoolean;\r
20 \r
21 import org.simantics.databoard.Bindings;\r
22 import org.simantics.databoard.binding.Binding;\r
23 import org.simantics.databoard.binding.VariantBinding;\r
24 import org.simantics.databoard.binding.error.BindingConstructionException;\r
25 import org.simantics.databoard.binding.error.BindingException;\r
26 import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;\r
27 import org.simantics.databoard.binding.mutable.Variant;\r
28 import org.simantics.databoard.type.Datatype;\r
29 import org.simantics.db.exception.DatabaseException;\r
30 import org.simantics.simulator.variable.NodeManager;\r
31 import org.simantics.simulator.variable.Realm;\r
32 import org.simantics.simulator.variable.exceptions.NoSuchNodeException;\r
33 import org.simantics.simulator.variable.exceptions.NodeManagerException;\r
34 import org.simantics.simulator.variable.exceptions.NotInRealmException;\r
35 \r
36 import gnu.trove.map.hash.THashMap;\r
37 import gnu.trove.procedure.TObjectProcedure;\r
38 import gnu.trove.set.hash.THashSet;\r
39 \r
40 /**\r
41  * StandardNodeManager gives default implementations to some methods\r
42  * of NodeManager.\r
43  * \r
44  * @author Antti Villberg\r
45  */\r
46 public abstract class StandardNodeManager<Node,Engine extends StandardEngine<Node>> implements NodeManager<Node> {\r
47         \r
48         final private Node root;\r
49         final private StandardRealm<Node,Engine> realm;\r
50 \r
51     final static Binding NO_BINDING = new VariantBinding() {\r
52 \r
53         @Override\r
54         public Object getContent(Object variant, Binding contentBinding) throws BindingException {\r
55             throw new Error();\r
56         }\r
57 \r
58         @Override\r
59         public Object getContent(Object variant) throws BindingException {\r
60             throw new Error();\r
61         }\r
62 \r
63         @Override\r
64         public Datatype getContentType(Object variant) throws BindingException {\r
65             throw new Error();\r
66         }\r
67 \r
68         @Override\r
69         public Binding getContentBinding(Object variant) throws BindingException {\r
70             throw new Error();\r
71         }\r
72 \r
73         @Override\r
74         public Object create(Binding contentBinding, Object content) throws BindingException {\r
75             throw new Error();\r
76         }\r
77 \r
78         @Override\r
79         public void setContent(Object variant, Binding contentBinding, Object content) throws BindingException {\r
80             throw new Error();\r
81         }\r
82 \r
83         @Override\r
84         public boolean isInstance(Object obj) {\r
85             return true;\r
86         }\r
87 \r
88         @Override\r
89         public void assertInstaceIsValid(Object obj, Set<Object> validInstances) throws BindingException {\r
90             throw new Error();\r
91         }\r
92         \r
93         @Override\r
94         public int compare(Object o1, Object o2) throws org.simantics.databoard.binding.error.RuntimeBindingException {\r
95                 if(o1 == null) {\r
96                         if(o2 == null) {\r
97                                 return 0;\r
98                         } else {\r
99                                 return  - System.identityHashCode(o2);\r
100                         }\r
101                 } else {\r
102                         if(o2 == null) {\r
103                                 return  System.identityHashCode(o1);\r
104                         } else {\r
105                                 if(o1.equals(o2)) return 0;\r
106                                 return System.identityHashCode(o1) - System.identityHashCode(o2);       \r
107                         }\r
108                 }\r
109         }\r
110 \r
111     };\r
112     \r
113     THashMap<Node, Object> valueCache = new THashMap<Node, Object>(); \r
114     protected THashMap<Node, THashSet<Runnable>> listeners = new THashMap<Node, THashSet<Runnable>>(); \r
115     \r
116     AtomicBoolean fireNodeListenersScheduled = new AtomicBoolean(false);\r
117     Runnable fireNodeListeners = new Runnable() {\r
118         @Override\r
119         public void run() {\r
120             fireNodeListenersScheduled.set(false);\r
121             final TObjectProcedure<Runnable> procedure = new TObjectProcedure<Runnable>() {\r
122                 @Override\r
123                 public boolean execute(Runnable object) {\r
124                     object.run();\r
125                     return true;\r
126                 }\r
127             };\r
128             synchronized(listeners) {\r
129                 listeners.forEachValue(new TObjectProcedure<THashSet<Runnable>>() {\r
130                     @Override\r
131                     public boolean execute(THashSet<Runnable> object) {\r
132                         object.forEach(procedure);\r
133                         return true;\r
134                     }\r
135                 });\r
136             }\r
137         }\r
138     };\r
139     \r
140     Runnable clearValueCache = new Runnable() {\r
141         @Override\r
142         public void run() {\r
143             valueCache.clear(); \r
144         }\r
145     };\r
146     \r
147     public StandardNodeManager(StandardRealm<Node,Engine> realm, Node root) {\r
148         this.realm = realm;\r
149         this.root = root;\r
150         }\r
151     \r
152         @Override\r
153         public List<String> getChildNames(Node node) throws NodeManagerException {\r
154                 List<Node> children = getChildren(node);\r
155                 ArrayList<String> names = new ArrayList<String>(children.size());\r
156                 for(Node child : children)\r
157                         names.add(getName(child));\r
158                 return names;\r
159         }\r
160         \r
161         @Override\r
162         public List<String> getPropertyNames(Node node) throws NodeManagerException {\r
163                 List<Node> properties = getProperties(node);\r
164                 ArrayList<String> names = new ArrayList<String>(properties.size());\r
165                 for(Node property : properties)\r
166                         names.add(getName(property));\r
167                 return names;\r
168         }\r
169         \r
170         @Override\r
171         public Object getValue(Node node, String propertyName, Binding binding)\r
172                         throws NodeManagerException, BindingException {\r
173                 Node property = getProperty(node, propertyName);\r
174                 if(property == null)\r
175                         throw new NoSuchNodeException("Didn't find a property " + propertyName);\r
176                 return getValue(property, binding);\r
177         }\r
178         \r
179         @Override\r
180         public void setValue(Node node, String propertyName, Object value,\r
181                         Binding binding) throws NodeManagerException, BindingException {\r
182                 Node property = getProperty(node, propertyName);\r
183                 if(property == null)\r
184                         throw new NoSuchNodeException("Didn't find a property " + propertyName);\r
185                 setValue(property, value, binding);\r
186         }\r
187         \r
188         @Override\r
189         public Variant getValue(Node node) throws NodeManagerException {\r
190                 Object value = getEngineValueOrCached(node);\r
191                 if (value instanceof Variant)\r
192                     return (Variant) value;\r
193         try {\r
194             Binding binding = Bindings.getBinding(value.getClass());\r
195             return new Variant(binding, value);\r
196         } catch (BindingConstructionException e) {\r
197             e.printStackTrace();\r
198             return null;\r
199         }\r
200         }\r
201         \r
202         @Override\r
203         public Variant getValue(Node node, String propertyName)\r
204                         throws NodeManagerException {\r
205                 Node property = getProperty(node, propertyName);\r
206                 if(property == null)\r
207                         throw new NoSuchNodeException("Didn't find a property " + propertyName);\r
208                 return getValue(property);\r
209         }\r
210         \r
211     @Override\r
212     public String getPropertyURI(Node parent, Node property) {\r
213         return null;\r
214     }\r
215     \r
216     @Override\r
217     public Realm getRealm() {\r
218         return realm;\r
219     }\r
220     \r
221     public StandardRealm<Node, Engine> getStandardRealm() {\r
222         return realm;\r
223     }\r
224     \r
225     protected String getRealmId() {\r
226         return realm.id;\r
227     }\r
228     \r
229     public Node getRoot() {\r
230         return root;\r
231     }\r
232     \r
233     protected boolean isRoot(Node node) {\r
234         return root.equals(node);\r
235     }\r
236 \r
237     @Override\r
238     public void addNodeListener(Node node, Runnable listener) {\r
239         synchronized(listeners) {\r
240             THashSet<Runnable> l = listeners.get(node);\r
241             if(l == null) {\r
242                 l = new THashSet<Runnable>();\r
243                 listeners.put(node, l);\r
244             }\r
245             l.add(listener);\r
246         }\r
247         getRealm().asyncExec(listener);\r
248     }\r
249 \r
250     @Override\r
251     public void removeNodeListener(Node node, Runnable listener) {\r
252         synchronized(listeners) {\r
253             THashSet<Runnable> l = listeners.get(node);\r
254             if(l != null) {\r
255                 l.remove(listener);\r
256                 if(l.isEmpty())\r
257                     listeners.remove(node);\r
258             }\r
259         }\r
260     }\r
261     \r
262     public void fireNodeListeners() {\r
263         if(!fireNodeListenersScheduled.getAndSet(true))\r
264             realm.asyncExec(fireNodeListeners);\r
265     }\r
266     \r
267     public void fireNodeListenersSync() {\r
268         try {\r
269                         realm.syncExec(fireNodeListeners);\r
270                 } catch (InterruptedException e) {\r
271                         e.printStackTrace();\r
272                 }\r
273     }\r
274 \r
275     public void refreshVariables() {\r
276         realm.asyncExec(clearValueCache);\r
277         fireNodeListeners();\r
278     }\r
279 \r
280     public void refreshVariablesSync() {\r
281         try {\r
282                         realm.syncExec(clearValueCache);\r
283                 } catch (InterruptedException e) {\r
284                         e.printStackTrace();\r
285                 }\r
286         fireNodeListenersSync();\r
287     }\r
288     \r
289     protected Object getEngineValueOrCached(Node node) throws NodeManagerException {\r
290         Object value = valueCache.get(node);\r
291         if(value == null) {\r
292                 value = realm.getEngine().getValue(node);\r
293             valueCache.put(node, value);\r
294         }\r
295         return value;\r
296     }\r
297     \r
298 \r
299     @Override\r
300     public Object getValue(Node node, Binding binding) throws NodeManagerException {\r
301         checkThreadAccess();\r
302         return getEngineValueOrCached(node);\r
303     }\r
304 \r
305     protected void checkThreadAccess() throws NodeManagerException {\r
306         if(Thread.currentThread() != realm.getThread())\r
307             throw new NotInRealmException();\r
308     }\r
309     \r
310     protected Datatype getDatatypeForValue(Object value) throws DatabaseException {\r
311         Binding binding = Bindings.getBindingUnchecked(value.getClass());\r
312         if(binding == null) return null;\r
313         else return binding.type();\r
314     }\r
315     \r
316     @Override\r
317     public void setValue(Node node, Object value, Binding binding)\r
318             throws NodeManagerException {\r
319         checkThreadAccess();\r
320         valueCache.put(node, value);\r
321         realm.getEngine().setValue(node, value);\r
322         realm.nodeManager.valueCache.put(node, value);\r
323         refreshVariables();\r
324     }\r
325     \r
326     @Override\r
327     public String getName(Node node) {\r
328         if(isRoot(node)) {\r
329                 String id = getRealmId();\r
330                 int lastSlash = id.lastIndexOf("/");\r
331                 if(lastSlash == -1) throw new IllegalStateException("Invalid realm id " + id);\r
332                 String name = id.substring(lastSlash+1); \r
333                 return name;\r
334         } else {\r
335                 return realm.getEngine().getName(node);\r
336         }\r
337     }\r
338     \r
339 \r
340     @Override\r
341     public Node getNode(String path) throws NodeManagerException {\r
342         checkThreadAccess();\r
343         throw new UnsupportedOperationException();\r
344     }\r
345     \r
346     @Override\r
347     public Node getChild(Node node, String name) throws NodeManagerException {\r
348         checkThreadAccess();\r
349         Map<String,Node> map = realm.getEngine().getChildren(node);\r
350         return map.get(name);\r
351     }\r
352 \r
353     @Override\r
354     public Node getProperty(Node node, String name) throws NodeManagerException {\r
355         checkThreadAccess();\r
356         Map<String,Node> map = realm.getEngine().getProperties(node);\r
357         return map.get(name);\r
358     }\r
359 \r
360     @Override\r
361     public List<Node> getChildren(Node node) throws NodeManagerException {\r
362         checkThreadAccess();\r
363         return new ArrayList<Node>(realm.getEngine().getChildren(node).values());\r
364     }\r
365 \r
366     @Override\r
367     public List<Node> getProperties(Node node) throws NodeManagerException {\r
368         checkThreadAccess();\r
369         return new ArrayList<Node>(realm.getEngine().getProperties(node).values());\r
370     }\r
371 \r
372     @Override\r
373     public Datatype getDatatype(Node node) throws NodeManagerException {\r
374         checkThreadAccess();\r
375         try {\r
376             Datatype type = getDatatypeForValue(getEngineValueOrCached(node));\r
377             return type;\r
378         } catch (DatabaseException e) {\r
379             e.printStackTrace();\r
380         } catch (RuntimeBindingConstructionException e) {\r
381             // There is no datatype for all values\r
382         }\r
383         return null;\r
384     }\r
385     \r
386 }\r