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