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