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