1 package org.simantics.scenegraph.loader;
\r
3 import java.lang.reflect.Field;
\r
4 import java.lang.reflect.Method;
\r
5 import java.util.ArrayList;
\r
6 import java.util.Collection;
\r
7 import java.util.Collections;
\r
8 import java.util.HashMap;
\r
9 import java.util.Map;
\r
11 import org.simantics.Simantics;
\r
12 import org.simantics.databoard.Bindings;
\r
13 import org.simantics.databoard.binding.Binding;
\r
14 import org.simantics.db.ReadGraph;
\r
15 import org.simantics.db.RequestProcessor;
\r
16 import org.simantics.db.Resource;
\r
17 import org.simantics.db.common.NamedResource;
\r
18 import org.simantics.db.common.procedure.adapter.ListenerAdapter;
\r
19 import org.simantics.db.common.request.BinaryRead;
\r
20 import org.simantics.db.common.request.ParametrizedPrimitiveRead;
\r
21 import org.simantics.db.common.request.ResourceRead;
\r
22 import org.simantics.db.common.request.UnaryRead;
\r
23 import org.simantics.db.exception.AssumptionException;
\r
24 import org.simantics.db.exception.DatabaseException;
\r
25 import org.simantics.db.layer0.exception.VariableException;
\r
26 import org.simantics.db.layer0.request.VariableName;
\r
27 import org.simantics.db.layer0.request.VariableURI;
\r
28 import org.simantics.db.layer0.variable.Variable;
\r
29 import org.simantics.db.layer0.variable.VariableBuilder;
\r
30 import org.simantics.db.layer0.variable.Variables;
\r
31 import org.simantics.db.procedure.Listener;
\r
32 import org.simantics.layer0.Layer0;
\r
33 import org.simantics.scenegraph.INode;
\r
34 import org.simantics.scenegraph.LoaderNode;
\r
35 import org.simantics.scenegraph.ParentNode;
\r
36 import org.simantics.scenegraph.ontology.ScenegraphResources;
\r
37 import org.simantics.scenegraph.utils.NodeUtil;
\r
38 import org.simantics.scl.runtime.function.Function1;
\r
39 import org.simantics.scl.runtime.function.FunctionImpl2;
\r
40 import org.simantics.utils.DataContainer;
\r
41 import org.simantics.utils.datastructures.Pair;
\r
42 import org.simantics.utils.threads.IThreadWorkQueue;
\r
43 import org.simantics.utils.threads.ThreadUtils;
\r
45 public class ScenegraphLoaderUtils {
\r
47 static Map<Pair<Variable, String>, Collection<Listener<Object>>> externalMap = new HashMap<Pair<Variable, String>, Collection<Listener<Object>>>();
\r
48 static Map<Pair<Variable, String>, Object> externalValueMap = new HashMap<Pair<Variable, String>, Object>();
\r
50 final public static class ScenegraphPropertyReference<T> {
\r
52 static class ExternalRead<T> extends ParametrizedPrimitiveRead<Pair<Variable, String>, T> {
\r
54 public ExternalRead(Variable base, String path) {
\r
55 super(Pair.make(base, path));
\r
59 public void register(ReadGraph graph, final Listener<T> procedure) {
\r
60 Object value = externalValueMap.get(parameter);
\r
61 procedure.execute((T)value);
\r
62 Collection<Listener<Object>> listeners = externalMap.get(parameter);
\r
63 if(listeners == null) {
\r
64 listeners = new ArrayList<Listener<Object>>();
\r
65 externalMap.put(parameter, listeners);
\r
67 listeners.add(new ListenerAdapter<Object>() {
\r
70 public void execute(Object result) {
\r
71 procedure.execute((T)result);
\r
78 public void unregistered() {
\r
79 externalMap.remove(parameter);
\r
84 Variable baseVariable;
\r
85 IThreadWorkQueue thread;
\r
90 public ScenegraphPropertyReference(IThreadWorkQueue thread, INode root, String reference, Variable baseVariable) {
\r
91 assert(root != null);
\r
92 this.thread = thread;
\r
94 this.reference = reference;
\r
95 this.baseVariable = baseVariable;
\r
98 public T getExternalValue(RequestProcessor processor) throws DatabaseException {
\r
99 return processor.sync(new ExternalRead<T>(baseVariable, reference));
\r
102 public T getValue() {
\r
104 final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);
\r
106 final DataContainer<T> result = new DataContainer<T>();
\r
108 ThreadUtils.syncExec(thread, new Runnable() {
\r
111 public void run() {
\r
112 T value = ScenegraphLoaderUtils.getNodeProperty((LoaderNode)ref.first, ref.second);
\r
118 return result.get();
\r
122 public void setValue(final T value) {
\r
124 final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);
\r
126 ThreadUtils.asyncExec(thread, new Runnable() {
\r
129 public void run() {
\r
130 Function1<Object, Boolean> function = ScenegraphLoaderUtils.getPropertyFunction((LoaderNode)ref.first, ref.second);
\r
131 if(function != null) {
\r
132 function.apply(value);
\r
134 new Exception("no function for ref " + ref).printStackTrace();
\r
140 //new Exception("no reference for " + root + " " + reference).printStackTrace();
\r
147 public static Collection<Variable> computeChildren(ReadGraph graph, Variable configuration) throws DatabaseException {
\r
148 ScenegraphResources SG = ScenegraphResources.getInstance(graph);
\r
149 Resource represents = configuration.getRepresents(graph);
\r
150 Collection<Resource> children = graph.getPossibleRelatedValue2(represents, SG.Node_children, configuration);
\r
151 if(children == null) return Collections.emptyList();
\r
152 ArrayList<Variable> result = new ArrayList<Variable>();
\r
153 for(Resource item : children) {
\r
154 VariableBuilder variableBuilder = graph.adapt(item, VariableBuilder.class);
\r
155 Variable child = variableBuilder.buildChild(graph, configuration, null, item);
\r
156 if(child != null) result.add(child);
\r
161 public static Collection<Variable> getChildren(RequestProcessor processor, Variable configuration) throws DatabaseException {
\r
163 return processor.sync(new UnaryRead<Variable, Collection<Variable>>(configuration) {
\r
166 public Collection<Variable> perform(ReadGraph graph) throws DatabaseException {
\r
167 return parameter.browseChildren(graph);
\r
174 public static Collection<Variable> getChildren(Resource configuration) throws DatabaseException {
\r
176 return Simantics.getSession().sync(new ResourceRead<Collection<Variable>>(configuration) {
\r
179 public Collection<Variable> perform(ReadGraph graph) throws DatabaseException {
\r
180 return computeChildren(graph, Variables.getVariable(graph, resource));
\r
187 public static Collection<NamedResource> getProperties(RequestProcessor processor, Resource configuration) throws DatabaseException {
\r
189 return processor.sync(new ResourceRead<Collection<NamedResource>>(configuration) {
\r
192 public Collection<NamedResource> perform(ReadGraph graph) throws DatabaseException {
\r
193 Layer0 L0 = Layer0.getInstance(graph);
\r
194 ScenegraphResources SG = ScenegraphResources.getInstance(graph);
\r
195 ArrayList<NamedResource> result = new ArrayList<NamedResource>();
\r
196 for(Resource predicate : graph.getPredicates(resource)) {
\r
197 if(graph.isSubrelationOf(predicate, SG.Node_HasProperty)) {
\r
198 String name = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);
\r
199 result.add(new NamedResource(name, predicate));
\r
210 * A custom exception for indicating that the a (runtime) resource has been
\r
211 * disposed of (i.e. its statements have been removed). Optimized by
\r
212 * nullifying {@link #fillInStackTrace()} since this is only used customly
\r
214 * {@link ScenegraphLoaderUtils#listen(RequestProcessor, Variable, String, Function1)}
\r
215 * to dispose of the DB listeners it creates.
\r
217 * @author Tuukka Lehtonen
\r
219 static class DisposedRuntimeException extends AssumptionException {
\r
221 private static final long serialVersionUID = 5213099691410928157L;
\r
223 public DisposedRuntimeException(String message) {
\r
228 public synchronized Throwable fillInStackTrace() {
\r
234 public static <T> void listen(RequestProcessor processor, final ScenegraphLoaderProcess process, final Variable context, final String property, final Function1<T, Boolean> function) throws DatabaseException {
\r
238 processor.syncRequest(new BinaryRead<Variable, String, T> (context, property) {
\r
240 @SuppressWarnings("unchecked")
\r
242 public T perform(ReadGraph graph) throws DatabaseException {
\r
243 // FIXME: this must throw a dedicated exception in the case where the runtime variable has been deleted which implies this listener should be disposed of
\r
244 SceneGraphContext vc = getContext(graph, context);
\r
246 throw new DisposedRuntimeException("No scene graph context");
\r
247 Resource runtime = vc.getRuntime();
\r
248 if (runtime == null || !graph.hasStatement(runtime))
\r
249 throw new DisposedRuntimeException("Scene graph runtime disposed");
\r
251 return (T)parameter.getPropertyValue(graph, parameter2);
\r
254 }, new Listener<T>() {
\r
256 private boolean disposed = false;
\r
259 public void exception(Throwable t) {
\r
260 if (t instanceof DisposedRuntimeException) {
\r
261 //System.out.println("ScenegraphLoaderUtils(" + this + ").listen: runtime disposed");
\r
264 //t.printStackTrace();
\r
269 public void execute(T result) {
\r
271 disposed = function.apply(result);
\r
275 public boolean isDisposed() {
\r
276 return process.isDisposed() | disposed;
\r
280 public String toString() {
\r
281 return "Scenegraph Property Listener for " + process;
\r
286 } catch (DatabaseException e) {
\r
292 public static Resource getRuntime(ReadGraph graph, Variable context) throws DatabaseException {
\r
293 SceneGraphContext vc = getContext(graph, context);
\r
294 if(vc != null) return vc.getRuntime();
\r
295 Variable parent = context.getParent(graph);
\r
296 if(parent == null) throw new DatabaseException("Runtime resource was not found from context Variable.");
\r
297 return getRuntime(graph, parent);
\r
300 public static SceneGraphContext getContext(ReadGraph graph, Variable context) throws DatabaseException {
\r
301 SceneGraphContext vc = context.adaptPossible(graph, SceneGraphContext.class);
\r
302 if(vc != null) return vc;
\r
304 Variable parent = context.getParent(graph);
\r
305 if(parent != null) return getContext(graph, parent);
\r
310 public static Variable getRuntimeVariable(ReadGraph graph, Variable context) throws DatabaseException {
\r
311 SceneGraphContext vc = getContext(graph, context);
\r
312 if(vc == null) return null;
\r
313 else return vc.getRuntimeVariable();
\r
316 public static Variable getBaseVariable(ReadGraph graph, Variable context) throws DatabaseException {
\r
318 Variable parent = context.getParent(graph);
\r
319 if(parent == null) return null;
\r
320 if(context instanceof ScenegraphVariable && !(parent instanceof ScenegraphVariable)) return context;
\r
321 else return getBaseVariable(graph, parent);
\r
325 static class ScenegraphReference extends UnaryRead<Variable, Pair<Variable, String>> {
\r
327 public ScenegraphReference(Variable var) {
\r
329 assert(var != null);
\r
333 public Pair<Variable, String> perform(ReadGraph graph) throws DatabaseException {
\r
334 Variable base = getBaseVariable(graph, parameter);
\r
335 return Pair.make(base, Variables.getRVI(graph, base, parameter));
\r
340 public static INode create(RequestProcessor processor, ScenegraphLoaderProcess process, ParentNode<?> parent, Resource configuration, final Variable context, Class<?> clazz) throws DatabaseException {
\r
342 final String name = processor.sync(new VariableName(context));
\r
344 final String uri = processor.sync(new VariableURI(context));
\r
346 LoaderNode node = (LoaderNode)parent.addNode(name, clazz);
\r
348 final Pair<Variable, String> reference = processor.sync(new ScenegraphReference(context));
\r
350 node.setPropertyCallback(new FunctionImpl2<String, Object, Boolean>() {
\r
353 public Boolean apply(String property, Object value) {
\r
354 Pair<Variable, String> key = Pair.make(reference.first, reference.second + "#" + property);
\r
355 externalValueMap.put(key, value);
\r
356 Collection<Listener<Object>> listeners = externalMap.get(key);
\r
357 if(listeners != null) {
\r
358 for(Listener<Object> listener : listeners) listener.execute(value);
\r
365 for(NamedResource property : ScenegraphLoaderUtils.getProperties(processor, configuration)) {
\r
367 Function1<Object, Boolean> func = node.getPropertyFunction(property.getName());
\r
369 ScenegraphLoaderUtils.listen(processor, process, context, property.getName(), func);
\r
371 // System.out.println("NO FUNCTION FOR PROPERTY: " + property.getName() + " (" + node + ")");
\r
372 } catch (Exception e) {
\r
373 e.printStackTrace();
\r
381 public static Variable getVariableSelection(ReadGraph graph, Variable context) throws DatabaseException {
\r
383 Variable runtimeVariable = getRuntimeVariable(graph, context);
\r
384 if (runtimeVariable == null)
\r
385 throw new VariableException("no runtime variable for context " + context.getURI(graph));
\r
386 return runtimeVariable.getPropertyValue(graph, "variable");
\r
390 public static Variable getPossibleVariableSelection(ReadGraph graph, Variable context) throws DatabaseException {
\r
391 Variable runtimeVariable = getRuntimeVariable(graph, context);
\r
392 return runtimeVariable == null ? null : (Variable) runtimeVariable.getPossiblePropertyValue(graph, "variable");
\r
395 public static Resource getResourceSelection(ReadGraph graph, Variable context) throws DatabaseException {
\r
397 Variable runtimeVariable = getRuntimeVariable(graph, context);
\r
398 if (runtimeVariable == null)
\r
399 throw new VariableException("no runtime variable for context " + context.getURI(graph));
\r
400 Resource sel = runtimeVariable.getPropertyValue(graph, "resource");
\r
405 public static Resource getPossibleResourceSelection(ReadGraph graph, Variable context) throws DatabaseException {
\r
407 Variable runtimeVariable = getRuntimeVariable(graph, context);
\r
408 return runtimeVariable == null ? null : (Resource) runtimeVariable.getPossiblePropertyValue(graph, "resource");
\r
412 public static INode getNode(ReadGraph graph, Variable location) throws DatabaseException {
\r
413 Variable runtime = getRuntimeVariable(graph, location);
\r
414 INode root = runtime.adapt(graph, INode.class);
\r
415 Variable base = getBaseVariable(graph, location);
\r
416 String rvi = Variables.getRVI(graph, base, location);
\r
417 return NodeUtil.browsePossible(root, rvi);
\r
420 // public static <T> ScenegraphPropertyReference<T> getPropertyReference(final IThreadWorkQueue thread, final Variable context, final String path) throws DatabaseException {
\r
421 // return Simantics.getSession().sync(new UniqueRead<ScenegraphPropertyReference<T>>() {
\r
424 // public ScenegraphPropertyReference<T> perform(ReadGraph graph) throws DatabaseException {
\r
425 // return getRelativePropertyReference(thread, graph, context, path);
\r
431 // public static <T> T getRelativeProperty(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {
\r
432 // ScenegraphPropertyReference<T> ref = getRelativePropertyReference(thread, graph, context, path);
\r
433 // return ref.getExternalValue(graph);
\r
436 public static <T> T getProperty(final IThreadWorkQueue thread, INode _root, String reference) {
\r
438 INode root = ((ParentNode<INode>)_root).getNodes().iterator().next();
\r
440 final Pair<INode, String> ref = NodeUtil.browsePossibleReference(root, reference);
\r
442 final DataContainer<T> result = new DataContainer<T>();
\r
444 ThreadUtils.syncExec(thread, new Runnable() {
\r
447 public void run() {
\r
448 T value = ScenegraphLoaderUtils.getNodeProperty((LoaderNode)ref.first, ref.second);
\r
454 return result.get();
\r
458 public static <T> ScenegraphPropertyReference<T> getRelativePropertyReference(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {
\r
460 Variable runtime = getRuntimeVariable(graph, context);
\r
461 INode root = runtime.adapt(graph, INode.class);
\r
462 Variable base = getBaseVariable(graph, context);
\r
463 INode baseNode = NodeUtil.findChildById((ParentNode)root, base.getName(graph));
\r
464 String contextRVI = Variables.getRVI(graph, base, context);
\r
465 String rvi = Variables.getRVI(contextRVI, path);
\r
466 return new ScenegraphPropertyReference<T>(thread, baseNode, rvi, base);
\r
470 public static <T> ScenegraphPropertyReference<T> getPropertyReference(final IThreadWorkQueue thread, ReadGraph graph, Variable context, String path) throws DatabaseException {
\r
472 Variable runtime = getRuntimeVariable(graph, context);
\r
473 INode root = runtime.adapt(graph, INode.class);
\r
474 Variable base = getBaseVariable(graph, context);
\r
475 return new ScenegraphPropertyReference<T>(thread, root, path, base);
\r
479 public static Method getSynchronizeMethod(INode node, String propertyName) {
\r
481 String methodName = "synchronize" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);
\r
482 for(Method m : node.getClass().getMethods()) {
\r
483 if(m.getName().equals(methodName)) return m;
\r
486 } catch (SecurityException e) {
\r
487 e.printStackTrace();
\r
492 public static Method getReadMethod(INode node, String propertyName) {
\r
494 String methodName = "read" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);
\r
495 return node.getClass().getMethod(methodName);
\r
496 } catch (SecurityException e) {
\r
497 e.printStackTrace();
\r
498 } catch (NoSuchMethodException e) {
\r
499 e.printStackTrace();
\r
504 public static Field getPropertyField(INode node, String propertyName) {
\r
506 return node.getClass().getField(propertyName);
\r
507 } catch (SecurityException e) {
\r
508 e.printStackTrace();
\r
509 } catch (NoSuchFieldException e) {
\r
510 System.err.println("node:" + node);
\r
511 e.printStackTrace();
\r
516 public static Class<?> getPropertyType(Field field) {
\r
517 return field.getType();
\r
520 public static Class<?> getArgumentType(Method method) {
\r
521 return (Class<?>)method.getGenericParameterTypes()[0];
\r
524 public static Class<?> getReturnType(Method method) {
\r
525 return method.getReturnType();
\r
528 public static Binding getPropertyBinding(Class<?> clazz) {
\r
530 return Bindings.getBindingUnchecked(clazz);
\r
531 } catch (Throwable t) {
\r
536 public static Binding getGenericPropertyBinding(Binding binding) {
\r
538 return Bindings.getBinding(binding.type());
\r
539 } catch (Throwable t) {
\r
544 public static Function1<Object, Boolean> getPropertyFunction(final LoaderNode node, final String propertyName) {
\r
545 return node.getPropertyFunction(propertyName);
\r
548 public static <T> T getNodeProperty(final LoaderNode node, final String propertyName) {
\r
549 return node.getProperty(propertyName);
\r
552 public static String getPath(ReadGraph graph, Variable context) throws DatabaseException {
\r
553 Variable base = getBaseVariable(graph, context);
\r
554 return Variables.getRVI(graph, base, context);
\r