-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.ListenerAdapter;
+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.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;
+
+public class ScenegraphLoaderUtils {
+
+ static Map<Pair<Variable, String>, Collection<Listener<Object>>> externalMap = new HashMap<Pair<Variable, String>, Collection<Listener<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<Listener<Object>> listeners = externalMap.get(parameter);
+ if(listeners == null) {
+ listeners = new ArrayList<Listener<Object>>();
+ externalMap.put(parameter, listeners);
+ }
+ listeners.add(new ListenerAdapter<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<Listener<Object>> listeners = externalMap.get(key);
+ if(listeners != null) {
+ for(Listener<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) {
+ e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ System.err.println("node:" + node);
+ e.printStackTrace();
+ }
+ 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);
+ }
+
+}