]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
diff --git a/bundles/org.simantics.scenegraph.loader/src/org/simantics/scenegraph/loader/ScenegraphLoaderUtils.java b/bundles/org.simantics.scenegraph.loader/src/org/simantics/scenegraph/loader/ScenegraphLoaderUtils.java
new file mode 100644 (file)
index 0000000..eddc083
--- /dev/null
@@ -0,0 +1,557 @@
+package org.simantics.scenegraph.loader;\r
+\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.Method;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.RequestProcessor;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.NamedResource;\r
+import org.simantics.db.common.procedure.adapter.ListenerAdapter;\r
+import org.simantics.db.common.request.BinaryRead;\r
+import org.simantics.db.common.request.ParametrizedPrimitiveRead;\r
+import org.simantics.db.common.request.ResourceRead;\r
+import org.simantics.db.common.request.UnaryRead;\r
+import org.simantics.db.exception.AssumptionException;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.exception.VariableException;\r
+import org.simantics.db.layer0.request.VariableName;\r
+import org.simantics.db.layer0.request.VariableURI;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.db.layer0.variable.VariableBuilder;\r
+import org.simantics.db.layer0.variable.Variables;\r
+import org.simantics.db.procedure.Listener;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.scenegraph.INode;\r
+import org.simantics.scenegraph.LoaderNode;\r
+import org.simantics.scenegraph.ParentNode;\r
+import org.simantics.scenegraph.ontology.ScenegraphResources;\r
+import org.simantics.scenegraph.utils.NodeUtil;\r
+import org.simantics.scl.runtime.function.Function1;\r
+import org.simantics.scl.runtime.function.FunctionImpl2;\r
+import org.simantics.utils.DataContainer;\r
+import org.simantics.utils.datastructures.Pair;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+public class ScenegraphLoaderUtils {\r
+       \r
+       static Map<Pair<Variable, String>, Collection<Listener<Object>>> externalMap = new HashMap<Pair<Variable, String>, Collection<Listener<Object>>>(); \r
+       static Map<Pair<Variable, String>, Object> externalValueMap = new HashMap<Pair<Variable, String>, Object>(); \r
+\r
+       final public static class ScenegraphPropertyReference<T> {\r
+\r
+               static class ExternalRead<T> extends ParametrizedPrimitiveRead<Pair<Variable, String>, T> {\r
+                       \r
+                       public ExternalRead(Variable base, String path) {\r
+                               super(Pair.make(base, path));\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void register(ReadGraph graph, final Listener<T> procedure) {\r
+                               Object value = externalValueMap.get(parameter);\r
+                               procedure.execute((T)value);\r
+                               Collection<Listener<Object>> listeners = externalMap.get(parameter);\r
+                               if(listeners == null) {\r
+                                       listeners = new ArrayList<Listener<Object>>();\r
+                                       externalMap.put(parameter, listeners);\r
+                               }\r
+                               listeners.add(new ListenerAdapter<Object>() {\r
+                                       \r
+                                       @Override\r
+                                       public void execute(Object result) {\r
+                                               procedure.execute((T)result);\r
+                                       }\r
+                                       \r
+                               });\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void unregistered() {\r
+                               externalMap.remove(parameter);\r
+                       }\r
+                       \r
+               }\r
+               \r
+               Variable baseVariable;\r
+               IThreadWorkQueue thread;\r
+               INode root;\r
+               String reference;\r
+               T value = null;\r
+               \r
+               public ScenegraphPropertyReference(IThreadWorkQueue thread, INode root, String reference, Variable baseVariable) {\r
+                       assert(root != null);\r
+                       this.thread = thread;\r
+                       this.root = root;\r
+                       this.reference = reference;\r
+                       this.baseVariable = baseVariable;\r
+               }\r
+               \r
+               public T getExternalValue(RequestProcessor processor) throws DatabaseException {\r
+                       return processor.sync(new ExternalRead<T>(baseVariable, reference));\r
+               }\r
+               \r
+               public T getValue() {\r
+\r
+                       final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);\r
+                       \r
+                       final DataContainer<T> result = new DataContainer<T>();\r
+                       \r
+                       ThreadUtils.syncExec(thread, new Runnable() {\r
+\r
+                               @Override\r
+                               public void run() {\r
+                                       T value = ScenegraphLoaderUtils.getNodeProperty((LoaderNode)ref.first, ref.second);\r
+                                       result.set(value);\r
+                               }\r
+                               \r
+                       });\r
+                       \r
+                       return result.get();\r
+                       \r
+               }\r
+\r
+               public void setValue(final T value) {\r
+                       \r
+                       final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);\r
+                       if(ref != null) {\r
+                               ThreadUtils.asyncExec(thread, new Runnable() {\r
+\r
+                                       @Override\r
+                                       public void run() {\r
+                                               Function1<Object, Boolean> function = ScenegraphLoaderUtils.getPropertyFunction((LoaderNode)ref.first, ref.second);\r
+                                               if(function != null) {\r
+                                                       function.apply(value);\r
+                                               } else {\r
+                                                       new Exception("no function for ref " + ref).printStackTrace();\r
+                                               }\r
+                                       }\r
+                                       \r
+                               });\r
+                       } else {\r
+                               //new Exception("no reference for " + root + " " + reference).printStackTrace();\r
+                       }\r
+                       \r
+                       \r
+               }               \r
+               \r
+       }       \r
+       public static Collection<Variable> computeChildren(ReadGraph graph, Variable configuration) throws DatabaseException {\r
+               ScenegraphResources SG = ScenegraphResources.getInstance(graph);\r
+               Resource represents = configuration.getRepresents(graph);\r
+               Collection<Resource> children = graph.getPossibleRelatedValue2(represents, SG.Node_children, configuration);\r
+               if(children == null) return Collections.emptyList();\r
+               ArrayList<Variable> result = new ArrayList<Variable>();\r
+               for(Resource item : children) {\r
+                       VariableBuilder variableBuilder = graph.adapt(item, VariableBuilder.class);\r
+                       Variable child = variableBuilder.buildChild(graph, configuration, null, item);\r
+                       if(child != null) result.add(child);\r
+               }\r
+               return result;\r
+       }\r
+       \r
+       public static Collection<Variable> getChildren(RequestProcessor processor, Variable configuration) throws DatabaseException {\r
+               \r
+               return processor.sync(new UnaryRead<Variable, Collection<Variable>>(configuration) {\r
+\r
+                       @Override\r
+                       public Collection<Variable> perform(ReadGraph graph) throws DatabaseException {\r
+                               return parameter.browseChildren(graph);\r
+                       }\r
+\r
+               });\r
+               \r
+       }\r
+\r
+       public static Collection<Variable> getChildren(Resource configuration) throws DatabaseException {\r
+               \r
+               return Simantics.getSession().sync(new ResourceRead<Collection<Variable>>(configuration) {\r
+\r
+                       @Override\r
+                       public Collection<Variable> perform(ReadGraph graph) throws DatabaseException {\r
+                               return computeChildren(graph, Variables.getVariable(graph, resource));\r
+                       }\r
+\r
+               });\r
+               \r
+       }\r
+\r
+       public static Collection<NamedResource> getProperties(RequestProcessor processor, Resource configuration) throws DatabaseException {\r
+               \r
+               return processor.sync(new ResourceRead<Collection<NamedResource>>(configuration) {\r
+\r
+                       @Override\r
+                       public Collection<NamedResource> perform(ReadGraph graph) throws DatabaseException {\r
+                               Layer0 L0 = Layer0.getInstance(graph);\r
+                               ScenegraphResources SG = ScenegraphResources.getInstance(graph);\r
+                               ArrayList<NamedResource> result = new ArrayList<NamedResource>(); \r
+                               for(Resource predicate : graph.getPredicates(resource)) {\r
+                                       if(graph.isSubrelationOf(predicate, SG.Node_HasProperty)) {\r
+                                               String name = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);\r
+                                               result.add(new NamedResource(name, predicate));\r
+                                       }\r
+                               }\r
+                               return result;\r
+                       }\r
+\r
+               });\r
+               \r
+       }\r
+\r
+       /**\r
+     * A custom exception for indicating that the a (runtime) resource has been\r
+     * disposed of (i.e. its statements have been removed). Optimized by\r
+     * nullifying {@link #fillInStackTrace()} since this is only used customly\r
+     * by\r
+     * {@link ScenegraphLoaderUtils#listen(RequestProcessor, Variable, String, Function1)}\r
+     * to dispose of the DB listeners it creates.\r
+     * \r
+     * @author Tuukka Lehtonen\r
+     */\r
+       static class DisposedRuntimeException extends AssumptionException {\r
+\r
+        private static final long serialVersionUID = 5213099691410928157L;\r
+\r
+        public DisposedRuntimeException(String message) {\r
+            super(message);\r
+        }\r
+\r
+        @Override\r
+        public synchronized Throwable fillInStackTrace() {\r
+            return this;\r
+        }\r
+\r
+       }\r
+\r
+       public static <T> void listen(RequestProcessor processor, final ScenegraphLoaderProcess process, final Variable context, final String property, final Function1<T, Boolean> function) throws DatabaseException {\r
+\r
+               try {\r
+               \r
+                       processor.syncRequest(new BinaryRead<Variable, String, T> (context, property) {\r
+\r
+                               @SuppressWarnings("unchecked")\r
+                               @Override\r
+                               public T perform(ReadGraph graph) throws DatabaseException {\r
+                                       // 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
+                                       SceneGraphContext vc = getContext(graph, context);\r
+                                       if (vc == null)\r
+                                               throw new DisposedRuntimeException("No scene graph context");\r
+                                       Resource runtime = vc.getRuntime();\r
+                                       if (runtime == null || !graph.hasStatement(runtime))\r
+                                               throw new DisposedRuntimeException("Scene graph runtime disposed");\r
+\r
+                                       return (T)parameter.getPropertyValue(graph, parameter2); \r
+                               }\r
+\r
+                       }, new Listener<T>() {\r
+\r
+                               private boolean disposed = false;\r
+\r
+                               @Override\r
+                               public void exception(Throwable t) {\r
+                                       if (t instanceof DisposedRuntimeException) {\r
+                                               //System.out.println("ScenegraphLoaderUtils(" + this + ").listen: runtime disposed");\r
+                                               disposed = true;\r
+                                       } else {\r
+                                               //t.printStackTrace();\r
+                                       }\r
+                               }\r
+\r
+                               @Override\r
+                               public void execute(T result) {\r
+                                       if (!disposed)\r
+                                               disposed = function.apply(result);\r
+                               }\r
+\r
+                               @Override\r
+                               public boolean isDisposed() {\r
+                                       return process.isDisposed() | disposed;\r
+                               }\r
+                               \r
+                               @Override\r
+                               public String toString() {\r
+                                       return "Scenegraph Property Listener for " + process;\r
+                               }\r
+\r
+                       });\r
+               \r
+               } catch (DatabaseException e) {\r
+                       \r
+               }\r
+    \r
+       }\r
+       \r
+    public static Resource getRuntime(ReadGraph graph, Variable context) throws DatabaseException {\r
+       SceneGraphContext vc = getContext(graph, context);\r
+       if(vc != null) return vc.getRuntime();\r
+       Variable parent = context.getParent(graph);\r
+       if(parent == null) throw new DatabaseException("Runtime resource was not found from context Variable.");\r
+       return getRuntime(graph, parent);\r
+    }\r
+    \r
+    public static SceneGraphContext getContext(ReadGraph graph, Variable context) throws DatabaseException {\r
+       SceneGraphContext vc = context.adaptPossible(graph, SceneGraphContext.class);\r
+       if(vc != null) return vc;\r
+       else {\r
+               Variable parent = context.getParent(graph);\r
+               if(parent != null) return getContext(graph, parent);\r
+               else return null;\r
+       }\r
+    }\r
+    \r
+    public static Variable getRuntimeVariable(ReadGraph graph, Variable context) throws DatabaseException {\r
+       SceneGraphContext vc = getContext(graph, context);\r
+       if(vc == null) return null;\r
+       else return vc.getRuntimeVariable();\r
+    }\r
+    \r
+    public static Variable getBaseVariable(ReadGraph graph, Variable context) throws DatabaseException {\r
+       \r
+       Variable parent = context.getParent(graph);\r
+       if(parent == null) return null;\r
+       if(context instanceof ScenegraphVariable && !(parent instanceof ScenegraphVariable)) return context;\r
+       else return getBaseVariable(graph, parent);\r
+       \r
+    }\r
+\r
+    static class ScenegraphReference extends UnaryRead<Variable, Pair<Variable, String>> {\r
+\r
+        public ScenegraphReference(Variable var) {\r
+               super(var);\r
+               assert(var != null);\r
+        }\r
+\r
+        @Override\r
+        public Pair<Variable, String> perform(ReadGraph graph) throws DatabaseException {\r
+               Variable base = getBaseVariable(graph, parameter);\r
+               return Pair.make(base, Variables.getRVI(graph, base, parameter));\r
+        }\r
+\r
+    }\r
+    \r
+    public static INode create(RequestProcessor processor, ScenegraphLoaderProcess process, ParentNode<?> parent, Resource configuration, final Variable context, Class<?> clazz) throws DatabaseException {\r
+\r
+       final String name = processor.sync(new VariableName(context));\r
+       \r
+       final String uri  = processor.sync(new VariableURI(context));\r
+       \r
+       LoaderNode node = (LoaderNode)parent.addNode(name, clazz);\r
+\r
+       final Pair<Variable, String> reference = processor.sync(new ScenegraphReference(context));\r
+\r
+       node.setPropertyCallback(new FunctionImpl2<String, Object, Boolean>() {\r
+\r
+               @Override\r
+               public Boolean apply(String property, Object value) {\r
+                       Pair<Variable, String> key = Pair.make(reference.first, reference.second + "#" + property);\r
+                       externalValueMap.put(key, value);\r
+                       Collection<Listener<Object>> listeners = externalMap.get(key);\r
+                       if(listeners != null) {\r
+                               for(Listener<Object> listener : listeners) listener.execute(value);\r
+                       }\r
+                       return true;\r
+               }\r
+               \r
+               });\r
+\r
+       for(NamedResource property : ScenegraphLoaderUtils.getProperties(processor, configuration)) {\r
+               try {\r
+                       Function1<Object, Boolean> func = node.getPropertyFunction(property.getName());\r
+                       if (func != null)\r
+                           ScenegraphLoaderUtils.listen(processor, process, context, property.getName(), func);\r
+                       //else\r
+                       //    System.out.println("NO FUNCTION FOR PROPERTY: " + property.getName() + " (" + node + ")");\r
+               } catch (Exception e) {\r
+                       e.printStackTrace();\r
+               }\r
+       }\r
+\r
+       return node;\r
+       \r
+    }\r
+       \r
+       public static Variable getVariableSelection(ReadGraph graph, Variable context) throws DatabaseException {\r
+               \r
+               Variable runtimeVariable = getRuntimeVariable(graph, context);\r
+               if (runtimeVariable == null)\r
+                       throw new VariableException("no runtime variable for context " + context.getURI(graph));\r
+               return runtimeVariable.getPropertyValue(graph, "variable");\r
+               \r
+       }\r
+\r
+       public static Variable getPossibleVariableSelection(ReadGraph graph, Variable context) throws DatabaseException {\r
+           Variable runtimeVariable = getRuntimeVariable(graph, context);\r
+           return runtimeVariable == null ? null : (Variable) runtimeVariable.getPossiblePropertyValue(graph, "variable");\r
+       }\r
+\r
+       public static Resource getResourceSelection(ReadGraph graph, Variable context) throws DatabaseException {\r
+\r
+               Variable runtimeVariable = getRuntimeVariable(graph, context);\r
+               if (runtimeVariable == null)\r
+            throw new VariableException("no runtime variable for context " + context.getURI(graph));\r
+               Resource sel = runtimeVariable.getPropertyValue(graph, "resource"); \r
+               return sel;\r
+\r
+       }\r
+       \r
+       public static Resource getPossibleResourceSelection(ReadGraph graph, Variable context) throws DatabaseException {\r
+\r
+           Variable runtimeVariable = getRuntimeVariable(graph, context);\r
+           return runtimeVariable == null ? null : (Resource) runtimeVariable.getPossiblePropertyValue(graph, "resource"); \r
+\r
+       }\r
+\r
+       public static INode getNode(ReadGraph graph, Variable location) throws DatabaseException {\r
+               Variable runtime = getRuntimeVariable(graph, location);\r
+               INode root = runtime.adapt(graph, INode.class);\r
+               Variable base = getBaseVariable(graph, location);\r
+               String rvi = Variables.getRVI(graph, base, location);\r
+               return NodeUtil.browsePossible(root, rvi);\r
+       }\r
+       \r
+//     public static <T> ScenegraphPropertyReference<T> getPropertyReference(final IThreadWorkQueue thread, final Variable context, final String path) throws DatabaseException {\r
+//             return Simantics.getSession().sync(new UniqueRead<ScenegraphPropertyReference<T>>() {\r
+//\r
+//                     @Override\r
+//                     public ScenegraphPropertyReference<T> perform(ReadGraph graph) throws DatabaseException {\r
+//                             return getRelativePropertyReference(thread, graph, context, path);\r
+//                     }\r
+//\r
+//             });\r
+//     }\r
+\r
+//     public static <T> T getRelativeProperty(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {\r
+//             ScenegraphPropertyReference<T> ref = getRelativePropertyReference(thread, graph, context, path);\r
+//             return ref.getExternalValue(graph);\r
+//     }\r
+\r
+       public static <T> T getProperty(final IThreadWorkQueue thread, INode _root, String reference) {\r
+               \r
+               INode root = ((ParentNode<INode>)_root).getNodes().iterator().next();\r
+               \r
+               final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);\r
+               \r
+               final DataContainer<T> result = new DataContainer<T>();\r
+               \r
+               ThreadUtils.syncExec(thread, new Runnable() {\r
+\r
+                       @Override\r
+                       public void run() {\r
+                               T value = ScenegraphLoaderUtils.getNodeProperty((LoaderNode)ref.first, ref.second);\r
+                               result.set(value);\r
+                       }\r
+                       \r
+               });\r
+               \r
+               return result.get();\r
+               \r
+       }\r
+\r
+       public static <T> ScenegraphPropertyReference<T> getRelativePropertyReference(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {\r
+               \r
+               Variable runtime = getRuntimeVariable(graph, context);\r
+               INode root = runtime.adapt(graph, INode.class);\r
+               Variable base = getBaseVariable(graph, context);\r
+               INode baseNode = NodeUtil.findChildById((ParentNode)root, base.getName(graph));\r
+               String contextRVI = Variables.getRVI(graph, base, context);\r
+               String rvi = Variables.getRVI(contextRVI, path);\r
+               return new ScenegraphPropertyReference<T>(thread, baseNode, rvi, base);\r
+               \r
+       }       \r
+\r
+       public static <T> ScenegraphPropertyReference<T> getPropertyReference(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {\r
+               \r
+               Variable runtime = getRuntimeVariable(graph, context);\r
+               INode root = runtime.adapt(graph, INode.class);\r
+               Variable base = getBaseVariable(graph, context);\r
+               return new ScenegraphPropertyReference<T>(thread, root, path, base);\r
+               \r
+       }       \r
+       \r
+       public static Method getSynchronizeMethod(INode node, String propertyName) {\r
+               try {\r
+                       String methodName = "synchronize" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);\r
+                       for(Method m : node.getClass().getMethods()) {\r
+                               if(m.getName().equals(methodName)) return m;\r
+                       }\r
+                       return null;\r
+               } catch (SecurityException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               return null;\r
+       }\r
+\r
+       public static Method getReadMethod(INode node, String propertyName) {\r
+               try {\r
+                       String methodName = "read" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);\r
+                       return node.getClass().getMethod(methodName);\r
+               } catch (SecurityException e) {\r
+                       e.printStackTrace();\r
+               } catch (NoSuchMethodException e) {\r
+                       e.printStackTrace();\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       public static Field getPropertyField(INode node, String propertyName) {\r
+               try {\r
+                       return node.getClass().getField(propertyName);\r
+               } catch (SecurityException e) {\r
+                       e.printStackTrace();\r
+               } catch (NoSuchFieldException e) {\r
+                       System.err.println("node:" + node);\r
+                       e.printStackTrace();\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       public static Class<?> getPropertyType(Field field) {\r
+               return field.getType();\r
+       }\r
+       \r
+       public static Class<?> getArgumentType(Method method) {\r
+               return (Class<?>)method.getGenericParameterTypes()[0];\r
+       }\r
+       \r
+       public static Class<?> getReturnType(Method method) {\r
+               return method.getReturnType();\r
+       }\r
+\r
+       public static Binding getPropertyBinding(Class<?> clazz) {\r
+               try {\r
+                       return Bindings.getBindingUnchecked(clazz);\r
+               } catch (Throwable t) {\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       public static Binding getGenericPropertyBinding(Binding binding) {\r
+               try {\r
+                       return Bindings.getBinding(binding.type());\r
+               } catch (Throwable t) {\r
+                       return null;\r
+               }\r
+       }\r
+       \r
+       public static Function1<Object, Boolean> getPropertyFunction(final LoaderNode node, final String propertyName) {\r
+               return node.getPropertyFunction(propertyName);\r
+       }\r
+\r
+       public static <T> T getNodeProperty(final LoaderNode node, final String propertyName) {\r
+               return node.getProperty(propertyName);\r
+       }\r
+       \r
+       public static String getPath(ReadGraph graph, Variable context) throws DatabaseException {\r
+               Variable base = getBaseVariable(graph, context);\r
+               return Variables.getRVI(graph, base, context);\r
+       }\r
+       \r
+}\r