X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scenegraph.loader%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Floader%2FScenegraphLoaderUtils.java;fp=bundles%2Forg.simantics.scenegraph.loader%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Floader%2FScenegraphLoaderUtils.java;h=eddc08347f37caf11f2e112645e0482fe91f3a5c;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08 diff --git a/bundles/org.simantics.scenegraph.loader/src/org/simantics/scenegraph/loader/ScenegraphLoaderUtils.java b/bundles/org.simantics.scenegraph.loader/src/org/simantics/scenegraph/loader/ScenegraphLoaderUtils.java new file mode 100644 index 000000000..eddc08347 --- /dev/null +++ b/bundles/org.simantics.scenegraph.loader/src/org/simantics/scenegraph/loader/ScenegraphLoaderUtils.java @@ -0,0 +1,557 @@ +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, Collection>> externalMap = new HashMap, Collection>>(); + static Map, Object> externalValueMap = new HashMap, Object>(); + + final public static class ScenegraphPropertyReference { + + static class ExternalRead extends ParametrizedPrimitiveRead, T> { + + public ExternalRead(Variable base, String path) { + super(Pair.make(base, path)); + } + + @Override + public void register(ReadGraph graph, final Listener procedure) { + Object value = externalValueMap.get(parameter); + procedure.execute((T)value); + Collection> listeners = externalMap.get(parameter); + if(listeners == null) { + listeners = new ArrayList>(); + externalMap.put(parameter, listeners); + } + listeners.add(new ListenerAdapter() { + + @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(baseVariable, reference)); + } + + public T getValue() { + + final Pair ref = NodeUtil.browsePossibleReference(root, reference); + + final DataContainer result = new DataContainer(); + + 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 ref = NodeUtil.browsePossibleReference(root, reference); + if(ref != null) { + ThreadUtils.asyncExec(thread, new Runnable() { + + @Override + public void run() { + Function1 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 computeChildren(ReadGraph graph, Variable configuration) throws DatabaseException { + ScenegraphResources SG = ScenegraphResources.getInstance(graph); + Resource represents = configuration.getRepresents(graph); + Collection children = graph.getPossibleRelatedValue2(represents, SG.Node_children, configuration); + if(children == null) return Collections.emptyList(); + ArrayList result = new ArrayList(); + 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 getChildren(RequestProcessor processor, Variable configuration) throws DatabaseException { + + return processor.sync(new UnaryRead>(configuration) { + + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + return parameter.browseChildren(graph); + } + + }); + + } + + public static Collection getChildren(Resource configuration) throws DatabaseException { + + return Simantics.getSession().sync(new ResourceRead>(configuration) { + + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + return computeChildren(graph, Variables.getVariable(graph, resource)); + } + + }); + + } + + public static Collection getProperties(RequestProcessor processor, Resource configuration) throws DatabaseException { + + return processor.sync(new ResourceRead>(configuration) { + + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + ScenegraphResources SG = ScenegraphResources.getInstance(graph); + ArrayList result = new ArrayList(); + 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 void listen(RequestProcessor processor, final ScenegraphLoaderProcess process, final Variable context, final String property, final Function1 function) throws DatabaseException { + + try { + + processor.syncRequest(new BinaryRead (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() { + + 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> { + + public ScenegraphReference(Variable var) { + super(var); + assert(var != null); + } + + @Override + public Pair 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 reference = processor.sync(new ScenegraphReference(context)); + + node.setPropertyCallback(new FunctionImpl2() { + + @Override + public Boolean apply(String property, Object value) { + Pair key = Pair.make(reference.first, reference.second + "#" + property); + externalValueMap.put(key, value); + Collection> listeners = externalMap.get(key); + if(listeners != null) { + for(Listener listener : listeners) listener.execute(value); + } + return true; + } + + }); + + for(NamedResource property : ScenegraphLoaderUtils.getProperties(processor, configuration)) { + try { + Function1 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 ScenegraphPropertyReference getPropertyReference(final IThreadWorkQueue thread, final Variable context, final String path) throws DatabaseException { +// return Simantics.getSession().sync(new UniqueRead>() { +// +// @Override +// public ScenegraphPropertyReference perform(ReadGraph graph) throws DatabaseException { +// return getRelativePropertyReference(thread, graph, context, path); +// } +// +// }); +// } + +// public static T getRelativeProperty(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException { +// ScenegraphPropertyReference ref = getRelativePropertyReference(thread, graph, context, path); +// return ref.getExternalValue(graph); +// } + + public static T getProperty(final IThreadWorkQueue thread, INode _root, String reference) { + + INode root = ((ParentNode)_root).getNodes().iterator().next(); + + final Pair ref = NodeUtil.browsePossibleReference(root, reference); + + final DataContainer result = new DataContainer(); + + 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 ScenegraphPropertyReference 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(thread, baseNode, rvi, base); + + } + + public static ScenegraphPropertyReference 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(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 getPropertyFunction(final LoaderNode node, final String propertyName) { + return node.getPropertyFunction(propertyName); + } + + public static 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); + } + +}