e41dd35da4bf934f16c736568aae8fed7e096c3e
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / scl / SCLNodeManager.java
1 package org.simantics.modeling.scl;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Set;
7 import java.util.concurrent.atomic.AtomicBoolean;
8
9 import org.simantics.databoard.Datatypes;
10 import org.simantics.databoard.binding.Binding;
11 import org.simantics.databoard.type.Datatype;
12 import org.simantics.db.exception.DatabaseException;
13 import org.simantics.db.layer0.variable.NodeSupport;
14 import org.simantics.modeling.ModelingResources;
15 import org.simantics.scl.compiler.types.Type;
16 import org.simantics.simulator.variable.Realm;
17 import org.simantics.simulator.variable.exceptions.NodeManagerException;
18 import org.simantics.simulator.variable.exceptions.NotInRealmException;
19 import org.simantics.simulator.variable.impl.AbstractNodeManager;
20 import org.simantics.structural.stubs.StructuralResource2;
21
22 import gnu.trove.map.hash.THashMap;
23 import gnu.trove.procedure.TObjectProcedure;
24 import gnu.trove.set.hash.THashSet;
25
26 public class SCLNodeManager extends AbstractNodeManager<String> {
27
28     public static final String ROOT = "@";
29     
30     SCLRealm realm;
31     THashMap<String, Object> valueCache = new THashMap<String, Object>(); 
32     THashMap<String, THashSet<Runnable>> listeners = new THashMap<String, THashSet<Runnable>>(); 
33     
34     AtomicBoolean fireNodeListenersScheduled = new AtomicBoolean(false);
35     Runnable fireNodeListeners = new Runnable() {
36         @Override
37         public void run() {
38             fireNodeListenersScheduled.set(false);
39             final TObjectProcedure<Runnable> procedure = new TObjectProcedure<Runnable>() {
40                 @Override
41                 public boolean execute(Runnable object) {
42                     object.run();
43                     return true;
44                 }
45             };
46             synchronized(listeners) {
47                 listeners.forEachValue(new TObjectProcedure<THashSet<Runnable>>() {
48                     @Override
49                     public boolean execute(THashSet<Runnable> object) {
50                         object.forEach(procedure);
51                         return true;
52                     }
53                 });
54             }
55         }
56     };
57     
58     Runnable clearValueCache = new Runnable() {
59         @Override
60         public void run() {
61             valueCache.clear(); 
62         }
63     };
64     
65     public SCLNodeManager(SCLRealm realm) {
66         super();
67         this.realm = realm;
68     }
69
70     @Override
71     public Realm getRealm() {
72         return realm;
73     }
74
75     public String getRoot() {
76         return ROOT;
77     }
78     
79     @Override
80     public String getName(String node) {
81         if(ROOT.equals(node)) {
82                 String id = realm.id;
83                 int lastSlash = id.lastIndexOf("/");
84                 if(lastSlash == -1) throw new IllegalStateException("Invalid realm id " + id);
85                 String name = id.substring(lastSlash+1); 
86                 return name;
87         } else
88             return node;
89     }
90
91     @Override
92     public void addNodeListener(String node, Runnable listener) {
93         synchronized(listeners) {
94             THashSet<Runnable> l = listeners.get(node);
95             if(l == null) {
96                 l = new THashSet<Runnable>();
97                 listeners.put(node, l);
98             }
99             l.add(listener);
100         }
101         realm.asyncExec(listener);
102     }
103
104     @Override
105     public void removeNodeListener(String node, Runnable listener) {
106         synchronized(listeners) {
107             THashSet<Runnable> l = listeners.get(node);
108             if(l != null) {
109                 l.remove(listener);
110                 if(l.isEmpty())
111                     listeners.remove(node);
112             }
113         }
114     }
115     
116     public void fireNodeListeners() {
117         if(!fireNodeListenersScheduled.getAndSet(true))
118             realm.asyncExec(fireNodeListeners);
119     }
120     
121     public void fireNodeListenersSync() {
122         try {
123                         realm.syncExec(fireNodeListeners);
124                 } catch (InterruptedException e) {
125                         e.printStackTrace();
126                 }
127     }
128
129     public void refreshVariables() {
130         realm.asyncExec(clearValueCache);
131         fireNodeListeners();
132     }
133
134     public void refreshVariablesSync() {
135         try {
136                         realm.syncExec(clearValueCache);
137                 } catch (InterruptedException e) {
138                         e.printStackTrace();
139                 }
140         fireNodeListenersSync();
141     }
142
143     @Override
144     public String getNode(String path) throws NodeManagerException {
145         checkThreadAccess();
146         throw new UnsupportedOperationException();
147     }
148
149     @Override
150     public String getChild(String node, String name)
151             throws NodeManagerException {
152         checkThreadAccess();
153         return null;
154     }
155
156     @Override
157     public String getProperty(String node, String name)
158             throws NodeManagerException {
159         checkThreadAccess();
160         if(node.equals(ROOT))
161             return name;
162         else
163             return null;
164     }
165
166     @Override
167     public List<String> getChildren(String node) throws NodeManagerException {
168         checkThreadAccess();
169         return Collections.emptyList();
170     }
171
172     @Override
173     public List<String> getProperties(String node) throws NodeManagerException {
174         checkThreadAccess();
175         if(!node.equals(ROOT))
176             return Collections.emptyList();
177                 
178         Set<String> variables = realm.getConnection().getVariables();
179         return new ArrayList<String>(variables);
180                 
181     }
182
183     @Override
184     public Datatype getDatatype(String node) throws NodeManagerException {
185         checkThreadAccess();
186         try {
187             Datatype type = getDatatypeForValue(getSCLValue(node));
188             return type;
189         } catch (DatabaseException e) {
190             e.printStackTrace();
191         }
192         return null;
193     }
194
195     @Override
196     public Object getValue(String node, Binding binding) throws NodeManagerException {
197         checkThreadAccess();
198         return getSCLValue(node);
199     }
200
201     
202     private Type getType(String name) {
203         return realm.getConnection().getVariableType(name);
204     }
205
206     private Datatype getDatatypeForValue(Object value) throws DatabaseException {
207         if(value instanceof Double) return Datatypes.DOUBLE;
208         if(value instanceof Float) return Datatypes.FLOAT;
209         if(value instanceof Integer) return Datatypes.INTEGER;
210         if(value instanceof Long) return Datatypes.LONG;
211         if(value instanceof String) return Datatypes.STRING;
212         if(value instanceof Boolean) return Datatypes.BOOLEAN;
213         
214         if (value instanceof List) return null;
215         
216         if (value instanceof Object) return null;
217         
218         if (value == null) return null;
219         
220         else throw new DatabaseException("No Datatype for value " + value);
221     }
222
223     @Override
224     public void setValue(String node, Object value, Binding binding)
225             throws NodeManagerException {
226         checkThreadAccess();
227         valueCache.put(node, value);
228         realm.getConnection().setVariable(node, getType(node), value);
229         realm.nodeManager.valueCache.put(node, value);
230         refreshVariables();
231     }
232
233     public void setValue(String node, Type type, Object value)
234             throws NodeManagerException {
235         
236         checkThreadAccess();
237         valueCache.put(node, value);
238         realm.getConnection().setVariable(node, type, value);
239         
240         NodeSupport support = SCLSessionManager.getOrCreateNodeSupport(realm.getId());
241         support.structureCache.put(ROOT, null);
242         support.valueCache.put(node, null);
243         
244         realm.nodeManager.valueCache.put(node, value);
245         realm.nodeManager.
246         refreshVariables();
247     }
248     
249     static final Set<String> COMPONENT_CLASS = Collections.singleton(StructuralResource2.URIs.Component);
250             
251     @Override
252     public Set<String> getClassifications(String node) throws NodeManagerException {
253         checkThreadAccess();
254         if(node.equals(ROOT))
255             return COMPONENT_CLASS;
256         else
257             return Collections.emptySet();
258     }
259
260     private Object getSCLValue(String node) throws NodeManagerException {
261         Object value = valueCache.get(node);
262         if(value == null) {
263                 value = realm.getConnection().getVariableValue(node);
264             valueCache.put(node, value);
265         }
266         return value;
267     }
268     
269     private void checkThreadAccess() throws NodeManagerException {
270         if(Thread.currentThread() != realm.getThread())
271             throw new NotInRealmException();
272     }
273     
274     @Override
275     public String getPropertyURI(String parent, String property) {
276         return ModelingResources.URIs.SCLCommandSession_hasValue;
277     }
278 }