]> gerrit.simantics Code Review - simantics/r.git/blob - org.simantics.r.scl/src/org/simantics/r/scl/variable/RNodeManager.java
Share project "org.simantics.r.feature" into "https://www.simantics.org/svn/simantics"
[simantics/r.git] / org.simantics.r.scl / src / org / simantics / r / scl / variable / RNodeManager.java
1 package org.simantics.r.scl.variable;\r
2 \r
3 import gnu.trove.map.hash.THashMap;\r
4 import gnu.trove.procedure.TObjectProcedure;\r
5 import gnu.trove.set.hash.THashSet;\r
6 \r
7 import java.util.ArrayList;\r
8 import java.util.Collections;\r
9 import java.util.List;\r
10 import java.util.Set;\r
11 import java.util.concurrent.atomic.AtomicBoolean;\r
12 \r
13 import org.rosuda.REngine.REXP;\r
14 import org.rosuda.REngine.REXPList;\r
15 import org.rosuda.REngine.REXPMismatchException;\r
16 import org.rosuda.REngine.REngineException;\r
17 import org.rosuda.REngine.RList;\r
18 import org.rosuda.REngine.Rserve.RserveException;\r
19 import org.simantics.databoard.Datatypes;\r
20 import org.simantics.databoard.binding.Binding;\r
21 import org.simantics.databoard.binding.error.BindingException;\r
22 import org.simantics.databoard.type.Datatype;\r
23 import org.simantics.r.scl.RSession;\r
24 import org.simantics.simulator.variable.Realm;\r
25 import org.simantics.simulator.variable.exceptions.NodeManagerException;\r
26 import org.simantics.simulator.variable.exceptions.NotInRealmException;\r
27 import org.simantics.simulator.variable.impl.AbstractNodeManager;\r
28 import org.simantics.structural.stubs.StructuralResource2;\r
29 import org.simantics.utils.datastructures.Pair;\r
30 \r
31 public class RNodeManager extends AbstractNodeManager<RVariableNode> implements RVariableNode {\r
32 \r
33         public final static String LENGTH_PROPERTY_NAME = "length";\r
34         public final static String ATTRIBUTE_NAME_PREFIX = "a-";\r
35         public final static String INDEXED_ITEM_NAME_PREFIX = "i-";\r
36         public final static String NAMED_ITEM_NAME_PREFIX = "n-";       \r
37         \r
38     RSession realm;\r
39     THashMap<RVariableNode, List<RVariableNode>> propertyCache = new THashMap<RVariableNode, List<RVariableNode>>(); \r
40     THashMap<Pair<RVariableNode, String>, RVariableNode> nodeCache = new THashMap<Pair<RVariableNode, String>, RVariableNode>(); \r
41     THashMap<RVariableNode, THashSet<Runnable>> listeners = new THashMap<RVariableNode, THashSet<Runnable>>();\r
42     List<RVariableNode> globals;\r
43     \r
44     AtomicBoolean fireNodeListenersScheduled = new AtomicBoolean(false);\r
45     Runnable fireNodeListeners = new Runnable() {\r
46         @Override\r
47         public void run() {\r
48             fireNodeListenersScheduled.set(false);\r
49             final TObjectProcedure<Runnable> procedure = new TObjectProcedure<Runnable>() {\r
50                 @Override\r
51                 public boolean execute(Runnable object) {\r
52                     object.run();\r
53                     return true;\r
54                 }\r
55             };\r
56             synchronized(listeners) {\r
57                 listeners.forEachValue(new TObjectProcedure<THashSet<Runnable>>() {\r
58                     @Override\r
59                     public boolean execute(THashSet<Runnable> object) {\r
60                         object.forEach(procedure);\r
61                         return true;\r
62                     }\r
63                 });\r
64             }\r
65         }\r
66     };\r
67     \r
68     Runnable clearValueCache = new Runnable() {\r
69         @Override\r
70         public void run() {\r
71             nodeCache.clear();\r
72             propertyCache.clear();\r
73             globals = null;\r
74         }\r
75     };\r
76     \r
77     public RNodeManager(RSession realm) {\r
78         super();\r
79         this.realm = realm;\r
80     }\r
81 \r
82     @Override\r
83     public Realm getRealm() {\r
84         return realm;\r
85     }\r
86 \r
87     @Override\r
88     public String getName(RVariableNode node) {\r
89         return node.getName();\r
90     }\r
91 \r
92     @Override\r
93     public void addNodeListener(RVariableNode node, Runnable listener) {\r
94         synchronized(listeners) {\r
95             THashSet<Runnable> l = listeners.get(node);\r
96             if(l == null) {\r
97                 l = new THashSet<Runnable>();\r
98                 listeners.put(node, l);\r
99             }\r
100             l.add(listener);\r
101         }\r
102         \r
103         if (realm.getThread() == Thread.currentThread())\r
104                 listener.run();\r
105         else\r
106                 realm.asyncExec(listener);\r
107     }\r
108 \r
109     @Override\r
110     public void removeNodeListener(RVariableNode node, Runnable listener) {\r
111         synchronized(listeners) {\r
112             THashSet<Runnable> l = listeners.get(node);\r
113             if(l != null) {\r
114                 l.remove(listener);\r
115                 if(l.isEmpty())\r
116                     listeners.remove(node);\r
117             }\r
118         }\r
119     }\r
120     \r
121     public void fireNodeListeners() {\r
122         if(!fireNodeListenersScheduled.getAndSet(true))\r
123             realm.asyncExec(fireNodeListeners);\r
124     }\r
125     \r
126     public void fireNodeListenersSync() {\r
127         try {\r
128                         realm.syncExec(fireNodeListeners);\r
129                 } catch (InterruptedException e) {\r
130                         e.printStackTrace();\r
131                 }\r
132     }\r
133 \r
134     public void refreshVariables() {\r
135         realm.asyncExec(clearValueCache);\r
136         fireNodeListeners();\r
137     }\r
138 \r
139     public void refreshVariablesSync() {\r
140         try {\r
141                         realm.syncExec(clearValueCache);\r
142                 } catch (InterruptedException e) {\r
143                         e.printStackTrace();\r
144                 }\r
145         fireNodeListenersSync();\r
146     }\r
147 \r
148     @Override\r
149     public RVariableNode getNode(String path) throws NodeManagerException {\r
150         checkThreadAccess();\r
151         throw new UnsupportedOperationException();\r
152     }\r
153 \r
154     @Override\r
155     public RVariableNode getChild(RVariableNode node, String name)\r
156             throws NodeManagerException {\r
157         checkThreadAccess();\r
158         return null;\r
159     }\r
160 \r
161     @Override\r
162     public RVariableNode getProperty(RVariableNode node, String name)\r
163             throws NodeManagerException {\r
164         checkThreadAccess();\r
165         RVariableNode prop = nodeCache.get(new Pair<RVariableNode, String>(node, name));\r
166         if (prop != null)\r
167                 return prop;\r
168 \r
169         if (node == this) {\r
170                 prop = new RGlobalVariableNode(this, name);\r
171         }\r
172         else if (node instanceof RListLengthNode) {\r
173                 return null;\r
174         }\r
175         else {\r
176                 if (name.equals(LENGTH_PROPERTY_NAME)) {\r
177                         prop = new RListLengthNode(node);\r
178                 }\r
179                 else if (name.startsWith(NAMED_ITEM_NAME_PREFIX)) {\r
180                         prop = new RNamedItemNode(node, name.substring(NAMED_ITEM_NAME_PREFIX.length()));\r
181                 }\r
182                 else if (name.startsWith(ATTRIBUTE_NAME_PREFIX)) {\r
183                         prop = new RAttributeNode(node, name.substring(ATTRIBUTE_NAME_PREFIX.length()));\r
184                 }\r
185                 else if (name.startsWith(INDEXED_ITEM_NAME_PREFIX)) {\r
186                         try {\r
187                                 int index = Integer.parseInt(name.substring(INDEXED_ITEM_NAME_PREFIX.length()));\r
188                                 if (index >= 0) {\r
189                                         prop = new RListItemNode(node, index);\r
190                                 }\r
191                         }\r
192                         catch (NumberFormatException e) {\r
193                                 return null;\r
194                         }\r
195                 }\r
196                 else {\r
197                         return null;\r
198                 }\r
199         }\r
200         \r
201         nodeCache.put(new Pair<RVariableNode, String>(node, name), prop);\r
202         \r
203         return prop;\r
204     }\r
205 \r
206     @Override\r
207     public List<RVariableNode> getChildren(RVariableNode node) throws NodeManagerException {\r
208         checkThreadAccess();\r
209         return Collections.emptyList();\r
210     }\r
211 \r
212     @Override\r
213     public List<RVariableNode> getProperties(RVariableNode node) throws NodeManagerException {\r
214         checkThreadAccess();\r
215         if (node == this) {\r
216                 if (globals != null)\r
217                         return globals;\r
218                 \r
219             try {               \r
220                 REXP result = realm.getConnection().eval("ls()");\r
221                 if(result.isString()) {\r
222                         String[] names = result.asStrings();                    \r
223                         \r
224                         globals = new ArrayList<RVariableNode>(names.length);\r
225                         for (String n : names) {\r
226                                 RGlobalVariableNode child = new RGlobalVariableNode(this, n);\r
227                                 globals.add(child);\r
228                                 nodeCache.put(new Pair<RVariableNode, String>(this, n), child);\r
229                         }\r
230                         return globals;\r
231                 }\r
232                 else\r
233                     throw new NodeManagerException("ls() returned invalid result!");\r
234             } catch (RserveException e) {\r
235                 throw new NodeManagerException(e);\r
236             } catch (REXPMismatchException e) {\r
237                 throw new NodeManagerException(e);\r
238             }\r
239         }\r
240         else if (node instanceof RListLengthNode) {\r
241                 return Collections.emptyList();\r
242         }\r
243         else {\r
244                 List<RVariableNode> props = propertyCache.get(node);\r
245                 if (props != null)\r
246                         return props;\r
247                 \r
248                 props = new ArrayList<RVariableNode>();\r
249                 \r
250                 REXP value = node.getValue();\r
251                 if (value == null)\r
252                         return Collections.emptyList();\r
253                 \r
254                 // Create attribute nodes\r
255                 REXPList attrs = value._attr();\r
256                 if (attrs != null) {\r
257                         RList list = attrs.asList();\r
258                         for (int i = 0; i < list.size(); i++)\r
259                                 props.add(new RAttributeNode(node, list.keyAt(i)));\r
260                 }\r
261                 \r
262                 // Create list item nodes\r
263                 if (value.isList()) {\r
264                         RList list;\r
265                                 try {\r
266                                         list = value.asList();\r
267                                 String[] keys = list.keys();\r
268                                 for (int i = 0; i < list.size(); i++) {\r
269                                         props.add(new RListItemNode(node, i));\r
270                                         if (keys != null && keys[i] != null)\r
271                                                 props.add(new RNamedItemNode(node, keys[i]));\r
272                                 }\r
273                                 } catch (REXPMismatchException e) {\r
274                                         // Shouldn't happen                                     \r
275                                 }\r
276                 }\r
277                 \r
278                 try {\r
279                         int length = value.length();            \r
280                         if (length >= 0)\r
281                                 props.add(new RListLengthNode(node));\r
282                 }\r
283                 catch (REXPMismatchException e) {\r
284                         // Does not have a length\r
285                 }\r
286                 \r
287                 propertyCache.put(node, props);\r
288                 \r
289                 for (RVariableNode n : props) {\r
290                         nodeCache.put(new Pair<RVariableNode, String>(node, n.getName()), n);\r
291                 }\r
292                 \r
293                 return props;\r
294         }\r
295     }\r
296 \r
297     @Override\r
298     public Datatype getDatatype(RVariableNode node) throws NodeManagerException {\r
299         checkThreadAccess();\r
300         if (node == this) return null;\r
301         \r
302         if (node instanceof RListLengthNode)\r
303                 return Datatypes.INTEGER;\r
304         \r
305         REXP value = node.getValue();\r
306         try {\r
307                         return RDataboardConversion.getDatatype(value);\r
308                 } catch (BindingException e) {\r
309                         throw new NodeManagerException(e);\r
310                 }\r
311     }\r
312 \r
313     @Override\r
314     public Object getValue(RVariableNode node, Binding binding)\r
315             throws NodeManagerException, BindingException {\r
316         checkThreadAccess();\r
317         if (node == this) return null;\r
318         \r
319         REXP value = node.getValue();\r
320         if (binding == null || binding.isInstance(value))\r
321                 return value;\r
322         else\r
323                 return RDataboardConversion.fromREXP(value, binding);\r
324     }\r
325 \r
326     @Override\r
327     public void setValue(RVariableNode node, Object value, Binding binding)\r
328             throws NodeManagerException, BindingException {\r
329         checkThreadAccess();\r
330         \r
331         if (node.getParent() != this)\r
332                 throw new NodeManagerException("Assignment only allowed on top-level nodes");\r
333         \r
334         String name = node.getName();\r
335         REXP rexp = RDataboardConversion.toREXP(value, binding);\r
336         try {\r
337             realm.getConnection().assign(name, rexp);\r
338             refreshVariables();\r
339         } catch (RserveException e) {\r
340             throw new NodeManagerException(e);\r
341         }\r
342     }\r
343 \r
344     static final Set<String> COMPONENT_CLASS = Collections.singleton(StructuralResource2.URIs.Component);\r
345             \r
346     @Override\r
347     public Set<String> getClassifications(RVariableNode node) throws NodeManagerException {\r
348         checkThreadAccess();\r
349         if(node.equals(this))\r
350             return COMPONENT_CLASS;\r
351         else\r
352             return Collections.emptySet();\r
353     }\r
354  \r
355     private void checkThreadAccess() throws NodeManagerException {\r
356         if(Thread.currentThread() != realm.getThread())\r
357             throw new NotInRealmException();\r
358     }\r
359     \r
360     @Override\r
361     public String getPropertyURI(RVariableNode parent, RVariableNode property) {\r
362         return "http://www.simantics.org/R-1.0/Session/hasValue";\r
363     }\r
364 \r
365     // Methods for the RVariableNode interface\r
366 \r
367         @Override\r
368         public String getName() {\r
369                 return realm.getId();\r
370         }\r
371 \r
372         @Override\r
373         public RVariableNode getParent() {\r
374                 return null;\r
375         }\r
376 \r
377         @Override\r
378         public REXP getValue() {\r
379                 return null;\r
380         }\r
381         \r
382         public REXP getGlobalValue(String name) {\r
383                 try {\r
384                         return realm.getConnection().get(name, null, true);\r
385                 } catch (REngineException e) {\r
386                         return null;\r
387                 }\r
388         }\r
389 }\r