]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.loader/src/org/simantics/scenegraph/loader/ScenegraphLoaderUtils.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph.loader / src / org / simantics / scenegraph / loader / ScenegraphLoaderUtils.java
1 package org.simantics.scenegraph.loader;\r
2 \r
3 import java.lang.reflect.Field;\r
4 import java.lang.reflect.Method;\r
5 import java.util.ArrayList;\r
6 import java.util.Collection;\r
7 import java.util.Collections;\r
8 import java.util.HashMap;\r
9 import java.util.Map;\r
10 \r
11 import org.simantics.Simantics;\r
12 import org.simantics.databoard.Bindings;\r
13 import org.simantics.databoard.binding.Binding;\r
14 import org.simantics.db.ReadGraph;\r
15 import org.simantics.db.RequestProcessor;\r
16 import org.simantics.db.Resource;\r
17 import org.simantics.db.common.NamedResource;\r
18 import org.simantics.db.common.procedure.adapter.ListenerAdapter;\r
19 import org.simantics.db.common.request.BinaryRead;\r
20 import org.simantics.db.common.request.ParametrizedPrimitiveRead;\r
21 import org.simantics.db.common.request.ResourceRead;\r
22 import org.simantics.db.common.request.UnaryRead;\r
23 import org.simantics.db.exception.AssumptionException;\r
24 import org.simantics.db.exception.DatabaseException;\r
25 import org.simantics.db.layer0.exception.VariableException;\r
26 import org.simantics.db.layer0.request.VariableName;\r
27 import org.simantics.db.layer0.request.VariableURI;\r
28 import org.simantics.db.layer0.variable.Variable;\r
29 import org.simantics.db.layer0.variable.VariableBuilder;\r
30 import org.simantics.db.layer0.variable.Variables;\r
31 import org.simantics.db.procedure.Listener;\r
32 import org.simantics.layer0.Layer0;\r
33 import org.simantics.scenegraph.INode;\r
34 import org.simantics.scenegraph.LoaderNode;\r
35 import org.simantics.scenegraph.ParentNode;\r
36 import org.simantics.scenegraph.ontology.ScenegraphResources;\r
37 import org.simantics.scenegraph.utils.NodeUtil;\r
38 import org.simantics.scl.runtime.function.Function1;\r
39 import org.simantics.scl.runtime.function.FunctionImpl2;\r
40 import org.simantics.utils.DataContainer;\r
41 import org.simantics.utils.datastructures.Pair;\r
42 import org.simantics.utils.threads.IThreadWorkQueue;\r
43 import org.simantics.utils.threads.ThreadUtils;\r
44 \r
45 public class ScenegraphLoaderUtils {\r
46         \r
47         static Map<Pair<Variable, String>, Collection<Listener<Object>>> externalMap = new HashMap<Pair<Variable, String>, Collection<Listener<Object>>>(); \r
48         static Map<Pair<Variable, String>, Object> externalValueMap = new HashMap<Pair<Variable, String>, Object>(); \r
49 \r
50         final public static class ScenegraphPropertyReference<T> {\r
51 \r
52                 static class ExternalRead<T> extends ParametrizedPrimitiveRead<Pair<Variable, String>, T> {\r
53                         \r
54                         public ExternalRead(Variable base, String path) {\r
55                                 super(Pair.make(base, path));\r
56                         }\r
57                         \r
58                         @Override\r
59                         public void register(ReadGraph graph, final Listener<T> procedure) {\r
60                                 Object value = externalValueMap.get(parameter);\r
61                                 procedure.execute((T)value);\r
62                                 Collection<Listener<Object>> listeners = externalMap.get(parameter);\r
63                                 if(listeners == null) {\r
64                                         listeners = new ArrayList<Listener<Object>>();\r
65                                         externalMap.put(parameter, listeners);\r
66                                 }\r
67                                 listeners.add(new ListenerAdapter<Object>() {\r
68                                         \r
69                                         @Override\r
70                                         public void execute(Object result) {\r
71                                                 procedure.execute((T)result);\r
72                                         }\r
73                                         \r
74                                 });\r
75                         }\r
76                         \r
77                         @Override\r
78                         public void unregistered() {\r
79                                 externalMap.remove(parameter);\r
80                         }\r
81                         \r
82                 }\r
83                 \r
84                 Variable baseVariable;\r
85                 IThreadWorkQueue thread;\r
86                 INode root;\r
87                 String reference;\r
88                 T value = null;\r
89                 \r
90                 public ScenegraphPropertyReference(IThreadWorkQueue thread, INode root, String reference, Variable baseVariable) {\r
91                         assert(root != null);\r
92                         this.thread = thread;\r
93                         this.root = root;\r
94                         this.reference = reference;\r
95                         this.baseVariable = baseVariable;\r
96                 }\r
97                 \r
98                 public T getExternalValue(RequestProcessor processor) throws DatabaseException {\r
99                         return processor.sync(new ExternalRead<T>(baseVariable, reference));\r
100                 }\r
101                 \r
102                 public T getValue() {\r
103 \r
104                         final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);\r
105                         \r
106                         final DataContainer<T> result = new DataContainer<T>();\r
107                         \r
108                         ThreadUtils.syncExec(thread, new Runnable() {\r
109 \r
110                                 @Override\r
111                                 public void run() {\r
112                                         T value = ScenegraphLoaderUtils.getNodeProperty((LoaderNode)ref.first, ref.second);\r
113                                         result.set(value);\r
114                                 }\r
115                                 \r
116                         });\r
117                         \r
118                         return result.get();\r
119                         \r
120                 }\r
121 \r
122                 public void setValue(final T value) {\r
123                         \r
124                         final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);\r
125                         if(ref != null) {\r
126                                 ThreadUtils.asyncExec(thread, new Runnable() {\r
127 \r
128                                         @Override\r
129                                         public void run() {\r
130                                                 Function1<Object, Boolean> function = ScenegraphLoaderUtils.getPropertyFunction((LoaderNode)ref.first, ref.second);\r
131                                                 if(function != null) {\r
132                                                         function.apply(value);\r
133                                                 } else {\r
134                                                         new Exception("no function for ref " + ref).printStackTrace();\r
135                                                 }\r
136                                         }\r
137                                         \r
138                                 });\r
139                         } else {\r
140                                 //new Exception("no reference for " + root + " " + reference).printStackTrace();\r
141                         }\r
142                         \r
143                         \r
144                 }               \r
145                 \r
146         }       \r
147         public static Collection<Variable> computeChildren(ReadGraph graph, Variable configuration) throws DatabaseException {\r
148                 ScenegraphResources SG = ScenegraphResources.getInstance(graph);\r
149                 Resource represents = configuration.getRepresents(graph);\r
150                 Collection<Resource> children = graph.getPossibleRelatedValue2(represents, SG.Node_children, configuration);\r
151                 if(children == null) return Collections.emptyList();\r
152                 ArrayList<Variable> result = new ArrayList<Variable>();\r
153                 for(Resource item : children) {\r
154                         VariableBuilder variableBuilder = graph.adapt(item, VariableBuilder.class);\r
155                         Variable child = variableBuilder.buildChild(graph, configuration, null, item);\r
156                         if(child != null) result.add(child);\r
157                 }\r
158                 return result;\r
159         }\r
160         \r
161         public static Collection<Variable> getChildren(RequestProcessor processor, Variable configuration) throws DatabaseException {\r
162                 \r
163                 return processor.sync(new UnaryRead<Variable, Collection<Variable>>(configuration) {\r
164 \r
165                         @Override\r
166                         public Collection<Variable> perform(ReadGraph graph) throws DatabaseException {\r
167                                 return parameter.browseChildren(graph);\r
168                         }\r
169 \r
170                 });\r
171                 \r
172         }\r
173 \r
174         public static Collection<Variable> getChildren(Resource configuration) throws DatabaseException {\r
175                 \r
176                 return Simantics.getSession().sync(new ResourceRead<Collection<Variable>>(configuration) {\r
177 \r
178                         @Override\r
179                         public Collection<Variable> perform(ReadGraph graph) throws DatabaseException {\r
180                                 return computeChildren(graph, Variables.getVariable(graph, resource));\r
181                         }\r
182 \r
183                 });\r
184                 \r
185         }\r
186 \r
187         public static Collection<NamedResource> getProperties(RequestProcessor processor, Resource configuration) throws DatabaseException {\r
188                 \r
189                 return processor.sync(new ResourceRead<Collection<NamedResource>>(configuration) {\r
190 \r
191                         @Override\r
192                         public Collection<NamedResource> perform(ReadGraph graph) throws DatabaseException {\r
193                                 Layer0 L0 = Layer0.getInstance(graph);\r
194                                 ScenegraphResources SG = ScenegraphResources.getInstance(graph);\r
195                                 ArrayList<NamedResource> result = new ArrayList<NamedResource>(); \r
196                                 for(Resource predicate : graph.getPredicates(resource)) {\r
197                                         if(graph.isSubrelationOf(predicate, SG.Node_HasProperty)) {\r
198                                                 String name = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);\r
199                                                 result.add(new NamedResource(name, predicate));\r
200                                         }\r
201                                 }\r
202                                 return result;\r
203                         }\r
204 \r
205                 });\r
206                 \r
207         }\r
208 \r
209         /**\r
210      * A custom exception for indicating that the a (runtime) resource has been\r
211      * disposed of (i.e. its statements have been removed). Optimized by\r
212      * nullifying {@link #fillInStackTrace()} since this is only used customly\r
213      * by\r
214      * {@link ScenegraphLoaderUtils#listen(RequestProcessor, Variable, String, Function1)}\r
215      * to dispose of the DB listeners it creates.\r
216      * \r
217      * @author Tuukka Lehtonen\r
218      */\r
219         static class DisposedRuntimeException extends AssumptionException {\r
220 \r
221         private static final long serialVersionUID = 5213099691410928157L;\r
222 \r
223         public DisposedRuntimeException(String message) {\r
224             super(message);\r
225         }\r
226 \r
227         @Override\r
228         public synchronized Throwable fillInStackTrace() {\r
229             return this;\r
230         }\r
231 \r
232         }\r
233 \r
234         public static <T> void listen(RequestProcessor processor, final ScenegraphLoaderProcess process, final Variable context, final String property, final Function1<T, Boolean> function) throws DatabaseException {\r
235 \r
236                 try {\r
237                 \r
238                         processor.syncRequest(new BinaryRead<Variable, String, T> (context, property) {\r
239 \r
240                                 @SuppressWarnings("unchecked")\r
241                                 @Override\r
242                                 public T perform(ReadGraph graph) throws DatabaseException {\r
243                                         // FIXME: this must throw a dedicated exception in the case where the runtime variable has been deleted which implies this listener should be disposed of\r
244                                         SceneGraphContext vc = getContext(graph, context);\r
245                                         if (vc == null)\r
246                                                 throw new DisposedRuntimeException("No scene graph context");\r
247                                         Resource runtime = vc.getRuntime();\r
248                                         if (runtime == null || !graph.hasStatement(runtime))\r
249                                                 throw new DisposedRuntimeException("Scene graph runtime disposed");\r
250 \r
251                                         return (T)parameter.getPropertyValue(graph, parameter2); \r
252                                 }\r
253 \r
254                         }, new Listener<T>() {\r
255 \r
256                                 private boolean disposed = false;\r
257 \r
258                                 @Override\r
259                                 public void exception(Throwable t) {\r
260                                         if (t instanceof DisposedRuntimeException) {\r
261                                                 //System.out.println("ScenegraphLoaderUtils(" + this + ").listen: runtime disposed");\r
262                                                 disposed = true;\r
263                                         } else {\r
264                                                 //t.printStackTrace();\r
265                                         }\r
266                                 }\r
267 \r
268                                 @Override\r
269                                 public void execute(T result) {\r
270                                         if (!disposed)\r
271                                                 disposed = function.apply(result);\r
272                                 }\r
273 \r
274                                 @Override\r
275                                 public boolean isDisposed() {\r
276                                         return process.isDisposed() | disposed;\r
277                                 }\r
278                                 \r
279                                 @Override\r
280                                 public String toString() {\r
281                                         return "Scenegraph Property Listener for " + process;\r
282                                 }\r
283 \r
284                         });\r
285                 \r
286                 } catch (DatabaseException e) {\r
287                         \r
288                 }\r
289     \r
290         }\r
291         \r
292     public static Resource getRuntime(ReadGraph graph, Variable context) throws DatabaseException {\r
293         SceneGraphContext vc = getContext(graph, context);\r
294         if(vc != null) return vc.getRuntime();\r
295         Variable parent = context.getParent(graph);\r
296         if(parent == null) throw new DatabaseException("Runtime resource was not found from context Variable.");\r
297         return getRuntime(graph, parent);\r
298     }\r
299     \r
300     public static SceneGraphContext getContext(ReadGraph graph, Variable context) throws DatabaseException {\r
301         SceneGraphContext vc = context.adaptPossible(graph, SceneGraphContext.class);\r
302         if(vc != null) return vc;\r
303         else {\r
304                 Variable parent = context.getParent(graph);\r
305                 if(parent != null) return getContext(graph, parent);\r
306                 else return null;\r
307         }\r
308     }\r
309     \r
310     public static Variable getRuntimeVariable(ReadGraph graph, Variable context) throws DatabaseException {\r
311         SceneGraphContext vc = getContext(graph, context);\r
312         if(vc == null) return null;\r
313         else return vc.getRuntimeVariable();\r
314     }\r
315     \r
316     public static Variable getBaseVariable(ReadGraph graph, Variable context) throws DatabaseException {\r
317         \r
318         Variable parent = context.getParent(graph);\r
319         if(parent == null) return null;\r
320         if(context instanceof ScenegraphVariable && !(parent instanceof ScenegraphVariable)) return context;\r
321         else return getBaseVariable(graph, parent);\r
322         \r
323     }\r
324 \r
325     static class ScenegraphReference extends UnaryRead<Variable, Pair<Variable, String>> {\r
326 \r
327         public ScenegraphReference(Variable var) {\r
328                 super(var);\r
329                 assert(var != null);\r
330         }\r
331 \r
332         @Override\r
333         public Pair<Variable, String> perform(ReadGraph graph) throws DatabaseException {\r
334                 Variable base = getBaseVariable(graph, parameter);\r
335                 return Pair.make(base, Variables.getRVI(graph, base, parameter));\r
336         }\r
337 \r
338     }\r
339     \r
340     public static INode create(RequestProcessor processor, ScenegraphLoaderProcess process, ParentNode<?> parent, Resource configuration, final Variable context, Class<?> clazz) throws DatabaseException {\r
341 \r
342         final String name = processor.sync(new VariableName(context));\r
343         \r
344         final String uri  = processor.sync(new VariableURI(context));\r
345         \r
346         LoaderNode node = (LoaderNode)parent.addNode(name, clazz);\r
347 \r
348         final Pair<Variable, String> reference = processor.sync(new ScenegraphReference(context));\r
349 \r
350         node.setPropertyCallback(new FunctionImpl2<String, Object, Boolean>() {\r
351 \r
352                 @Override\r
353                 public Boolean apply(String property, Object value) {\r
354                         Pair<Variable, String> key = Pair.make(reference.first, reference.second + "#" + property);\r
355                         externalValueMap.put(key, value);\r
356                         Collection<Listener<Object>> listeners = externalMap.get(key);\r
357                         if(listeners != null) {\r
358                                 for(Listener<Object> listener : listeners) listener.execute(value);\r
359                         }\r
360                         return true;\r
361                 }\r
362                 \r
363                 });\r
364 \r
365         for(NamedResource property : ScenegraphLoaderUtils.getProperties(processor, configuration)) {\r
366                 try {\r
367                         Function1<Object, Boolean> func = node.getPropertyFunction(property.getName());\r
368                         if (func != null)\r
369                             ScenegraphLoaderUtils.listen(processor, process, context, property.getName(), func);\r
370                         //else\r
371                         //    System.out.println("NO FUNCTION FOR PROPERTY: " + property.getName() + " (" + node + ")");\r
372                 } catch (Exception e) {\r
373                         e.printStackTrace();\r
374                 }\r
375         }\r
376 \r
377         return node;\r
378         \r
379     }\r
380         \r
381         public static Variable getVariableSelection(ReadGraph graph, Variable context) throws DatabaseException {\r
382                 \r
383                 Variable runtimeVariable = getRuntimeVariable(graph, context);\r
384                 if (runtimeVariable == null)\r
385                         throw new VariableException("no runtime variable for context " + context.getURI(graph));\r
386                 return runtimeVariable.getPropertyValue(graph, "variable");\r
387                 \r
388         }\r
389 \r
390         public static Variable getPossibleVariableSelection(ReadGraph graph, Variable context) throws DatabaseException {\r
391             Variable runtimeVariable = getRuntimeVariable(graph, context);\r
392             return runtimeVariable == null ? null : (Variable) runtimeVariable.getPossiblePropertyValue(graph, "variable");\r
393         }\r
394 \r
395         public static Resource getResourceSelection(ReadGraph graph, Variable context) throws DatabaseException {\r
396 \r
397                 Variable runtimeVariable = getRuntimeVariable(graph, context);\r
398                 if (runtimeVariable == null)\r
399             throw new VariableException("no runtime variable for context " + context.getURI(graph));\r
400                 Resource sel = runtimeVariable.getPropertyValue(graph, "resource"); \r
401                 return sel;\r
402 \r
403         }\r
404         \r
405         public static Resource getPossibleResourceSelection(ReadGraph graph, Variable context) throws DatabaseException {\r
406 \r
407             Variable runtimeVariable = getRuntimeVariable(graph, context);\r
408             return runtimeVariable == null ? null : (Resource) runtimeVariable.getPossiblePropertyValue(graph, "resource"); \r
409 \r
410         }\r
411 \r
412         public static INode getNode(ReadGraph graph, Variable location) throws DatabaseException {\r
413                 Variable runtime = getRuntimeVariable(graph, location);\r
414                 INode root = runtime.adapt(graph, INode.class);\r
415                 Variable base = getBaseVariable(graph, location);\r
416                 String rvi = Variables.getRVI(graph, base, location);\r
417                 return NodeUtil.browsePossible(root, rvi);\r
418         }\r
419         \r
420 //      public static <T> ScenegraphPropertyReference<T> getPropertyReference(final IThreadWorkQueue thread, final Variable context, final String path) throws DatabaseException {\r
421 //              return Simantics.getSession().sync(new UniqueRead<ScenegraphPropertyReference<T>>() {\r
422 //\r
423 //                      @Override\r
424 //                      public ScenegraphPropertyReference<T> perform(ReadGraph graph) throws DatabaseException {\r
425 //                              return getRelativePropertyReference(thread, graph, context, path);\r
426 //                      }\r
427 //\r
428 //              });\r
429 //      }\r
430 \r
431 //      public static <T> T getRelativeProperty(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {\r
432 //              ScenegraphPropertyReference<T> ref = getRelativePropertyReference(thread, graph, context, path);\r
433 //              return ref.getExternalValue(graph);\r
434 //      }\r
435 \r
436         public static <T> T getProperty(final IThreadWorkQueue thread, INode _root, String reference) {\r
437                 \r
438                 INode root = ((ParentNode<INode>)_root).getNodes().iterator().next();\r
439                 \r
440                 final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);\r
441                 \r
442                 final DataContainer<T> result = new DataContainer<T>();\r
443                 \r
444                 ThreadUtils.syncExec(thread, new Runnable() {\r
445 \r
446                         @Override\r
447                         public void run() {\r
448                                 T value = ScenegraphLoaderUtils.getNodeProperty((LoaderNode)ref.first, ref.second);\r
449                                 result.set(value);\r
450                         }\r
451                         \r
452                 });\r
453                 \r
454                 return result.get();\r
455                 \r
456         }\r
457 \r
458         public static <T> ScenegraphPropertyReference<T> getRelativePropertyReference(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {\r
459                 \r
460                 Variable runtime = getRuntimeVariable(graph, context);\r
461                 INode root = runtime.adapt(graph, INode.class);\r
462                 Variable base = getBaseVariable(graph, context);\r
463                 INode baseNode = NodeUtil.findChildById((ParentNode)root, base.getName(graph));\r
464                 String contextRVI = Variables.getRVI(graph, base, context);\r
465                 String rvi = Variables.getRVI(contextRVI, path);\r
466                 return new ScenegraphPropertyReference<T>(thread, baseNode, rvi, base);\r
467                 \r
468         }       \r
469 \r
470         public static <T> ScenegraphPropertyReference<T> getPropertyReference(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {\r
471                 \r
472                 Variable runtime = getRuntimeVariable(graph, context);\r
473                 INode root = runtime.adapt(graph, INode.class);\r
474                 Variable base = getBaseVariable(graph, context);\r
475                 return new ScenegraphPropertyReference<T>(thread, root, path, base);\r
476                 \r
477         }       \r
478         \r
479         public static Method getSynchronizeMethod(INode node, String propertyName) {\r
480                 try {\r
481                         String methodName = "synchronize" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);\r
482                         for(Method m : node.getClass().getMethods()) {\r
483                                 if(m.getName().equals(methodName)) return m;\r
484                         }\r
485                         return null;\r
486                 } catch (SecurityException e) {\r
487                         e.printStackTrace();\r
488                 }\r
489                 return null;\r
490         }\r
491 \r
492         public static Method getReadMethod(INode node, String propertyName) {\r
493                 try {\r
494                         String methodName = "read" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);\r
495                         return node.getClass().getMethod(methodName);\r
496                 } catch (SecurityException e) {\r
497                         e.printStackTrace();\r
498                 } catch (NoSuchMethodException e) {\r
499                         e.printStackTrace();\r
500                 }\r
501                 return null;\r
502         }\r
503         \r
504         public static Field getPropertyField(INode node, String propertyName) {\r
505                 try {\r
506                         return node.getClass().getField(propertyName);\r
507                 } catch (SecurityException e) {\r
508                         e.printStackTrace();\r
509                 } catch (NoSuchFieldException e) {\r
510                         System.err.println("node:" + node);\r
511                         e.printStackTrace();\r
512                 }\r
513                 return null;\r
514         }\r
515         \r
516         public static Class<?> getPropertyType(Field field) {\r
517                 return field.getType();\r
518         }\r
519         \r
520         public static Class<?> getArgumentType(Method method) {\r
521                 return (Class<?>)method.getGenericParameterTypes()[0];\r
522         }\r
523         \r
524         public static Class<?> getReturnType(Method method) {\r
525                 return method.getReturnType();\r
526         }\r
527 \r
528         public static Binding getPropertyBinding(Class<?> clazz) {\r
529                 try {\r
530                         return Bindings.getBindingUnchecked(clazz);\r
531                 } catch (Throwable t) {\r
532                         return null;\r
533                 }\r
534         }\r
535         \r
536         public static Binding getGenericPropertyBinding(Binding binding) {\r
537                 try {\r
538                         return Bindings.getBinding(binding.type());\r
539                 } catch (Throwable t) {\r
540                         return null;\r
541                 }\r
542         }\r
543         \r
544         public static Function1<Object, Boolean> getPropertyFunction(final LoaderNode node, final String propertyName) {\r
545                 return node.getPropertyFunction(propertyName);\r
546         }\r
547 \r
548         public static <T> T getNodeProperty(final LoaderNode node, final String propertyName) {\r
549                 return node.getProperty(propertyName);\r
550         }\r
551         \r
552         public static String getPath(ReadGraph graph, Variable context) throws DatabaseException {\r
553                 Variable base = getBaseVariable(graph, context);\r
554                 return Variables.getRVI(graph, base, context);\r
555         }\r
556         \r
557 }\r