-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.diagram.profile;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.concurrent.ConcurrentHashMap;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.RequestProcessor;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
-import org.simantics.db.common.request.TernaryRead;\r
-import org.simantics.db.common.request.UnaryRead;\r
-import org.simantics.db.common.utils.NameUtils;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.variable.Variable;\r
-import org.simantics.db.procedure.Listener;\r
-import org.simantics.db.request.Read;\r
-import org.simantics.diagram.stubs.DiagramResource;\r
-import org.simantics.g2d.canvas.ICanvasContext;\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.scenegraph.INode;\r
-import org.simantics.scenegraph.profile.DataNodeMap;\r
-import org.simantics.scenegraph.profile.EvaluationContext;\r
-import org.simantics.scenegraph.profile.Group;\r
-import org.simantics.scenegraph.profile.Observer;\r
-import org.simantics.scenegraph.profile.Style;\r
-import org.simantics.scenegraph.profile.common.ObserverGroupListener;\r
-import org.simantics.scenegraph.profile.common.ObserverGroupValueListener;\r
-import org.simantics.scenegraph.profile.impl.DebugPolicy;\r
-import org.simantics.scl.runtime.tuple.Tuple;\r
-import org.simantics.scl.runtime.tuple.Tuple2;\r
-import org.simantics.utils.datastructures.Pair;\r
-import org.simantics.utils.threads.AWTThread;\r
-\r
-/**\r
- * For most style implementations it should be enough to override the following\r
- * methods:\r
- * <ul>\r
- * <li>{@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}</li>\r
- * <li>{@link #applyStyleForElement(Observer, Object, IElement, Object)}</li>\r
- * <li>{@link #cleanupStyleForElement(IDiagram, IElement)}</li>\r
- * </ul>\r
- * \r
- * <p>\r
- * See each method for a specification of what they are intended for.\r
- * \r
- * Optionally you can also override\r
- * {@link #styleResultChanged(Observer, Resource, Object)} for optimization\r
- * purposes but this is usually not necessary.\r
- * \r
- * @author Tuukka Lehtonen\r
- * f\r
- * @param <Result> type of result objects for styled group items tracked by this\r
- * style implementation, see\r
- * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}\r
- */\r
-public abstract class StyleBase<Result> implements Style {\r
-\r
- protected final Map<Tuple, Result> values = new ConcurrentHashMap<Tuple, Result>();\r
-\r
-// private Map<Resource,ObserverGroupListener> listeners = new ConcurrentHashMap<Resource, ObserverGroupListener>();\r
-\r
- private Map<Pair<Resource, Group>, ObserverGroupListener> listeners = new HashMap<Pair<Resource, Group>, ObserverGroupListener>();\r
- \r
-\r
- private final List<Resource> removals = new ArrayList<Resource>();\r
-\r
- /**\r
- * For caching this simple base request that is done in every\r
- * {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource)}.\r
- */\r
- static class RuntimeDiagramVariableRequest extends UnaryRead<Resource, Variable> {\r
- public RuntimeDiagramVariableRequest(Resource runtimeDiagram) {\r
- super(runtimeDiagram);\r
- }\r
-\r
- @Override\r
- public Variable perform(ReadGraph graph) throws DatabaseException {\r
- DiagramResource DIA = DiagramResource.getInstance(graph);\r
- String variableURI = graph.getPossibleRelatedValue(parameter, DIA.RuntimeDiagram_HasVariable, Bindings.STRING);\r
- if (variableURI == null)\r
- return null;\r
- Variable activeVariable = org.simantics.db.layer0.variable.Variables.getPossibleVariable(graph, variableURI);\r
- return activeVariable;\r
- }\r
- }\r
-\r
- /**\r
- * Calculates a typed result to be used for applying the style to a diagram\r
- * in\r
- * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}\r
- * . The graph request system will take care of only notifying other systems\r
- * when the style result actually changes.\r
- * \r
- * <p>\r
- * This implementation uses {@link RuntimeDiagramVariableRequest} to\r
- * discover the active composite variable related to the specified\r
- * runtimeDiagram and invokes\r
- * {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}\r
- * with it.\r
- * \r
- * @param graph\r
- * database read access\r
- * @param runtimeDiagram\r
- * resource describing the runtime data of the styled diagram\r
- * @param entry\r
- * profile entry resource, don't care if now aware\r
- * @param groupItem\r
- * an item belonging to the observed group\r
- * @return the calculated style result object\r
- * @throws DatabaseException\r
- * @see {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}\r
- */\r
- public Result calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException {\r
- Variable activeVariable = graph.syncRequest(new RuntimeDiagramVariableRequest(runtimeDiagram), TransientCacheListener.<Variable>instance());\r
- if (activeVariable == null)\r
- return null;\r
-\r
- //System.out.println("URI1: " + configuration.getURI(graph));\r
- //System.out.println("URI2: " + activeVariable.getURI(graph));\r
- return calculateStyle(graph, runtimeDiagram, entry, groupItem, activeVariable);\r
- }\r
-\r
- /**\r
- * Calculates a typed result to be used for applying the style to a diagram\r
- * in\r
- * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}\r
- * . The graph request system will take care of only notifying other systems\r
- * when the style result actually changes.\r
- * \r
- * @param graph database read access\r
- * @param runtimeDiagram resource describing the runtime data of the styled\r
- * diagram\r
- * @param entry profile entry resource, don't care if now aware\r
- * @param groupItem an item belonging to the observed group\r
- * @param activeComposite variable for accessing the active realization of the\r
- * diagram's corresponding composite. This may change when\r
- * experiments are activate and deactivated. When there is no\r
- * experiment active, this is the base realization, i.e. the\r
- * configuration.\r
- * @return the calculated style result object\r
- * @throws DatabaseException\r
- * @see {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}\r
- */\r
- public Result calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem, Variable activeComposite) throws DatabaseException {\r
- return null;\r
- }\r
-\r
- /**\r
- * Invoked when the result calculated by\r
- * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)} changes.\r
- * Used for keeping track of the latest calculated style values that are\r
- * applied in\r
- * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}.\r
- * \r
- * @param observer\r
- * @param object\r
- * @param result\r
- */\r
- public void styleResultChanged(Observer observer, Resource runtimeDiagram, Resource object, Result result) {\r
- if (result == null)\r
- values.remove(new Tuple2(runtimeDiagram, object));\r
- else\r
- values.put(new Tuple2(runtimeDiagram, object), result);\r
- observer.update();\r
- }\r
-\r
- /**\r
- * Apply the latest style result calculated by\r
- * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)} to the\r
- * scene graph of the specified diagram item (i.e. element).\r
- * \r
- * <p>\r
- * <code>StyleBase</code> ensures that this method is invoked in the AWT\r
- * thread only.\r
- * \r
- * @param observer profile system observer\r
- * @param item the styled diagram item data\r
- * @param element the styled diagram element representing the data item\r
- * @param result the latest result calculated by\r
- * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}\r
- */\r
- public void applyStyleForNode(EvaluationContext evaluationContext, INode node, Result result) {\r
- }\r
- \r
- public void applyStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item, Result value) {\r
- \r
- final INode node = map.getNode(item);\r
- if (node == null) {\r
- evaluationContext.update();\r
- // TODO: continue or return?\r
- return;\r
- }\r
-\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_APPLICATION)\r
- System.out.println(StyleBase.this + ": applying style for item " + item + " and element " + node + " with result " + value);\r
-\r
- applyStyleForNode(evaluationContext, node, value);\r
- \r
- }\r
-\r
- /**\r
- * This method is invoked by\r
- * {@link #cleanupStyleForNode(EvaluationContext, INode)} when the style is\r
- * deactivated. It is invoked for each diagram element tracked by the style\r
- * before deactivation.\r
- * \r
- * @param node a previously tracked and styled scene graph node\r
- */\r
- protected void cleanupStyleForNode(INode node) {\r
- }\r
-\r
- /**\r
- * This method is invoked by\r
- * {@link #cleanupStyleForItem(EvaluationContext, DataNodeMap, Object)} when the style is\r
- * deactivated. It is invoked for each diagram element tracked by the style\r
- * before deactivation.\r
- * @param evaluationContext the context of this style evaluation\r
- * @param node a previously tracked and styled scene graph node\r
- */\r
- protected void cleanupStyleForNode(EvaluationContext evaluationContext, INode node) {\r
- cleanupStyleForNode(node);\r
- }\r
-\r
- protected void cleanupStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item) {\r
-\r
- final INode node = map.getNode(item);\r
- if (node != null) {\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)\r
- System.out.println(this + ".cleanupStyleForItem(" + item + " = " + node + ")");\r
- cleanupStyleForNode(evaluationContext, node);\r
- }\r
-\r
- }\r
- \r
- \r
- static class GroupListener<T> extends ObserverGroupListener {\r
- \r
- private StyleBase<T> style;\r
- private Session session;\r
- private Resource runtimeDiagram;\r
- private Resource entry;\r
- \r
- GroupListener(Session session, Resource runtimeDiagram, Resource entry, StyleBase<T> style, Group group, Observer observer) {\r
- super(style, group, observer);\r
- this.style = style;\r
- this.session = session;\r
- this.runtimeDiagram = runtimeDiagram;\r
- this.entry = entry;\r
- }\r
- \r
- @Override\r
- public void add(final Resource item) {\r
-\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)\r
- System.out.println(style + ": added to group " + group + ": " + item);\r
-\r
- session.asyncRequest(\r
- style.getStyleCalculationRequest(runtimeDiagram, entry, item),\r
- style.getStyleResultListener(this, item, group, observer, runtimeDiagram)\r
- );\r
-\r
- super.add(item);\r
- }\r
- @Override\r
- public void remove(Resource item) {\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)\r
- System.out.println(style + ": removed from group " + group + ": " + item);\r
-\r
- synchronized (style.removals) {\r
- style.removals.add(item);\r
- }\r
-\r
- // TODO: do something here to dispose of ObserverGroupValueListeners?\r
- super.remove(item);\r
- } \r
- \r
- }\r
- \r
- /* (non-Javadoc)\r
- * @see org.simantics.diagram.profile.Style#activate(org.simantics.db.RequestProcessor, org.simantics.db.Resource, org.simantics.db.layer0.variable.Variable, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)\r
- */\r
- @Override\r
- public final void activate(RequestProcessor backend, final Resource runtimeDiagram, final Resource entry, final Group group, final EvaluationContext observer) {\r
-\r
- ObserverGroupListener listener = getListener(runtimeDiagram, group);\r
-\r
- if (listener == null || listener.isDisposed()) {\r
-\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)\r
- System.out.println("activate(" + runtimeDiagram + ", " + group + ", " + observer);\r
-\r
- listener = new GroupListener<Result>(backend.getSession(), runtimeDiagram, entry, this, group, observer);\r
-\r
- listeners.put(Pair.make(runtimeDiagram, group), listener);\r
-\r
- group.trackItems(backend, runtimeDiagram, listener);\r
-\r
- }\r
-\r
- // Register this entry in the listener\r
- listener.addEntry(entry);\r
- }\r
-\r
- /**\r
- * Used to customize the identity given to graph requests made for this\r
- * style. Default identity is getClass().\r
- * \r
- * @return identity object used in graph requests made by this style\r
- */\r
- protected Object getIdentity(Resource entry) {\r
- return new Pair<Class<?>, Resource>(getClass(), entry);\r
- }\r
-\r
- /**\r
- * @param configuration\r
- * @param runtimeDiagram\r
- * @param item\r
- * @return\r
- */\r
- protected Read<Result> getStyleCalculationRequest(Resource runtimeDiagram, final Resource entry, Resource item) {\r
- return new TernaryRead<Object, Resource, Resource, Result>(getIdentity(entry), runtimeDiagram, item) {\r
- @Override\r
- public Result perform(ReadGraph graph) throws DatabaseException {\r
- boolean oldSynchronous = graph.getSynchronous();\r
- try {\r
- graph.setSynchronous(false);\r
- Result result = calculateStyle(graph, parameter2, entry, parameter3);\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)\r
- System.out.println(StyleBase.this + ": calculated style result for " + NameUtils.getSafeName(graph, parameter3, true) + ": " + result);\r
- return result;\r
- } finally {\r
- graph.setSynchronous(oldSynchronous);\r
- }\r
- }\r
- };\r
- }\r
-\r
- /**\r
- * @param groupListener\r
- * @param item\r
- * @param group\r
- * @param observer\r
- * @param runtimeDiagram \r
- * @return\r
- */\r
- protected Listener<Result> getStyleResultListener(ObserverGroupListener groupListener, final Resource item,\r
- Group group, Observer observer, final Resource runtimeDiagram) {\r
- return new ObserverGroupValueListener<Result>(groupListener, observer, group, item) {\r
- @Override\r
- public void execute(Result result) {\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)\r
- System.out.println(StyleBase.this + ": style result changed for " + item + ": " + result);\r
- styleResultChanged(observer, runtimeDiagram, item, result);\r
- }\r
- };\r
- }\r
-\r
- /* (non-Javadoc)\r
- * @see org.simantics.diagram.profile.Style#deactivate(org.simantics.db.RequestProcessor, org.simantics.db.Resource, org.simantics.db.layer0.variable.Variable, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)\r
- */\r
- @Override\r
- public final void deactivate(Resource runtimeDiagram, Resource entry, Group group,\r
- EvaluationContext observer) {\r
-\r
- ObserverGroupListener listener = getListener(runtimeDiagram, group);\r
- if (listener != null) {\r
-\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)\r
- System.out.println("deactivate(" + runtimeDiagram + ", " + group + ", " + observer);\r
-\r
- IDiagram diagram = observer.getConstant(ProfileKeys.DIAGRAM);\r
-\r
- listener.removeEntry(entry);\r
- if (!listener.hasEntries()) {\r
- listener.dispose();\r
- listeners.remove(Pair.make(runtimeDiagram, group));\r
- }\r
-\r
- // This was too eager when multiple groups were tracked!\r
- //values.clear();\r
- if (diagram != null) {\r
- cleanupItems(observer, diagram, listener.getItems().toArray());\r
- diagram = null;\r
- }\r
- observer.update();\r
- }\r
-\r
- }\r
-\r
- /* (non-Javadoc)\r
- * @see org.simantics.diagram.profile.Style#apply(org.simantics.g2d.diagram.IDiagram, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)\r
- */\r
- @Override\r
- public final void apply(Resource entry, Group group, final EvaluationContext evaluationContext) {\r
-\r
- ICanvasContext context = evaluationContext.getConstant(ProfileKeys.CANVAS);\r
- \r
- assert context.getThreadAccess().currentThreadAccess();\r
-\r
- ObserverGroupListener listener = getListener(evaluationContext.getResource(), group);\r
- if (listener == null) {\r
- System.out.println(this + "(" + getClass().getSimpleName() + ") had no listener for " + evaluationContext.getResource() + " " + group);\r
- return;\r
- }\r
- \r
- final DataNodeMap map = evaluationContext.getConstant(ProfileKeys.NODE_MAP);\r
-\r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_APPLICATION)\r
- System.out.println(StyleBase.this + ": applying style for items: " + listener.getItems());\r
-\r
- if (!removals.isEmpty()) {\r
- Resource[] removed;\r
- synchronized (removals) {\r
- removed = removals.toArray(Resource.NONE);\r
- removals.clear();\r
- }\r
- for (Resource item : removed) {\r
- cleanupStyleForItem(evaluationContext, map, item);\r
- }\r
- }\r
- \r
- for (Object item : listener.getItems()) {\r
- Result value = values.get(new Tuple2(evaluationContext.getResource(), item));\r
- applyStyleForItem(evaluationContext, map, item, value);\r
- }\r
- \r
- }\r
-\r
- /**\r
- * This is ran when this profile entry gets deactivated after being first\r
- * active. It allows cleaning up scene graph left-overs for the listened set\r
- * of items before deactivation. It will invoke\r
- * {@link #cleanupStyleForElement(IDiagram, IElement)} for each diagram element observed\r
- * before deactivation in the AWT thread. If the profile observer is\r
- * disposed in between scheduling to AWT thread, the method will do nothing.\r
- * \r
- * @param observer the diagram profile observer\r
- * @param diagram the diagram this profile system is working with\r
- * @param items the diagram data items that need to be cleaned up\r
- */\r
- protected final void cleanupItems(final EvaluationContext evaluationContext, final IDiagram diagram, final Object[] items) {\r
- AWTThread.getThreadAccess().asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- \r
- if (evaluationContext.isDisposed())\r
- return;\r
-\r
- final DataNodeMap map = evaluationContext.getConstant(ProfileKeys.NODE_MAP);\r
- \r
- if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)\r
- System.out.println(this + ".cleanupItems(" + evaluationContext + ", " + diagram + ", " + Arrays.toString(items));\r
-\r
- for (Object item : items) {\r
- cleanupStyleForItem(evaluationContext, map, item);\r
- }\r
- }\r
- });\r
- }\r
- \r
- private ObserverGroupListener getListener(Resource runtime, Group group) {\r
- return listeners.get(Pair.make(runtime, group));\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.diagram.profile;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.TernaryRead;
+import org.simantics.db.common.request.UnaryRead;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.procedure.Listener;
+import org.simantics.db.request.Read;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.element.IElement;
+import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.profile.DataNodeMap;
+import org.simantics.scenegraph.profile.EvaluationContext;
+import org.simantics.scenegraph.profile.Group;
+import org.simantics.scenegraph.profile.Observer;
+import org.simantics.scenegraph.profile.Style;
+import org.simantics.scenegraph.profile.common.ObserverGroupListener;
+import org.simantics.scenegraph.profile.common.ObserverGroupValueListener;
+import org.simantics.scenegraph.profile.impl.DebugPolicy;
+import org.simantics.scl.runtime.tuple.Tuple;
+import org.simantics.scl.runtime.tuple.Tuple2;
+import org.simantics.utils.datastructures.Pair;
+import org.simantics.utils.threads.AWTThread;
+
+/**
+ * For most style implementations it should be enough to override the following
+ * methods:
+ * <ul>
+ * <li>{@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}</li>
+ * <li>{@link #applyStyleForElement(Observer, Object, IElement, Object)}</li>
+ * <li>{@link #cleanupStyleForElement(IDiagram, IElement)}</li>
+ * </ul>
+ *
+ * <p>
+ * See each method for a specification of what they are intended for.
+ *
+ * Optionally you can also override
+ * {@link #styleResultChanged(Observer, Resource, Object)} for optimization
+ * purposes but this is usually not necessary.
+ *
+ * @author Tuukka Lehtonen
+ * f
+ * @param <Result> type of result objects for styled group items tracked by this
+ * style implementation, see
+ * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}
+ */
+public abstract class StyleBase<Result> implements Style {
+
+ protected final Map<Tuple, Result> values = new ConcurrentHashMap<Tuple, Result>();
+
+// private Map<Resource,ObserverGroupListener> listeners = new ConcurrentHashMap<Resource, ObserverGroupListener>();
+
+ private Map<Pair<Resource, Group>, ObserverGroupListener> listeners = new HashMap<Pair<Resource, Group>, ObserverGroupListener>();
+
+
+ private final List<Resource> removals = new ArrayList<Resource>();
+
+ /**
+ * For caching this simple base request that is done in every
+ * {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource)}.
+ */
+ static class RuntimeDiagramVariableRequest extends UnaryRead<Resource, Variable> {
+ public RuntimeDiagramVariableRequest(Resource runtimeDiagram) {
+ super(runtimeDiagram);
+ }
+
+ @Override
+ public Variable perform(ReadGraph graph) throws DatabaseException {
+ DiagramResource DIA = DiagramResource.getInstance(graph);
+ String variableURI = graph.getPossibleRelatedValue(parameter, DIA.RuntimeDiagram_HasVariable, Bindings.STRING);
+ if (variableURI == null)
+ return null;
+ Variable activeVariable = org.simantics.db.layer0.variable.Variables.getPossibleVariable(graph, variableURI);
+ return activeVariable;
+ }
+ }
+
+ /**
+ * Calculates a typed result to be used for applying the style to a diagram
+ * in
+ * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}
+ * . The graph request system will take care of only notifying other systems
+ * when the style result actually changes.
+ *
+ * <p>
+ * This implementation uses {@link RuntimeDiagramVariableRequest} to
+ * discover the active composite variable related to the specified
+ * runtimeDiagram and invokes
+ * {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}
+ * with it.
+ *
+ * @param graph
+ * database read access
+ * @param runtimeDiagram
+ * resource describing the runtime data of the styled diagram
+ * @param entry
+ * profile entry resource, don't care if now aware
+ * @param groupItem
+ * an item belonging to the observed group
+ * @return the calculated style result object
+ * @throws DatabaseException
+ * @see {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}
+ */
+ public Result calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException {
+ Variable activeVariable = graph.syncRequest(new RuntimeDiagramVariableRequest(runtimeDiagram), TransientCacheListener.<Variable>instance());
+ if (activeVariable == null)
+ return null;
+
+ //System.out.println("URI1: " + configuration.getURI(graph));
+ //System.out.println("URI2: " + activeVariable.getURI(graph));
+ return calculateStyle(graph, runtimeDiagram, entry, groupItem, activeVariable);
+ }
+
+ /**
+ * Calculates a typed result to be used for applying the style to a diagram
+ * in
+ * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}
+ * . The graph request system will take care of only notifying other systems
+ * when the style result actually changes.
+ *
+ * @param graph database read access
+ * @param runtimeDiagram resource describing the runtime data of the styled
+ * diagram
+ * @param entry profile entry resource, don't care if now aware
+ * @param groupItem an item belonging to the observed group
+ * @param activeComposite variable for accessing the active realization of the
+ * diagram's corresponding composite. This may change when
+ * experiments are activate and deactivated. When there is no
+ * experiment active, this is the base realization, i.e. the
+ * configuration.
+ * @return the calculated style result object
+ * @throws DatabaseException
+ * @see {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}
+ */
+ public Result calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem, Variable activeComposite) throws DatabaseException {
+ return null;
+ }
+
+ /**
+ * Invoked when the result calculated by
+ * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)} changes.
+ * Used for keeping track of the latest calculated style values that are
+ * applied in
+ * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}.
+ *
+ * @param observer
+ * @param object
+ * @param result
+ */
+ public void styleResultChanged(Observer observer, Resource runtimeDiagram, Resource object, Result result) {
+ if (result == null)
+ values.remove(new Tuple2(runtimeDiagram, object));
+ else
+ values.put(new Tuple2(runtimeDiagram, object), result);
+ observer.update();
+ }
+
+ /**
+ * Apply the latest style result calculated by
+ * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)} to the
+ * scene graph of the specified diagram item (i.e. element).
+ *
+ * <p>
+ * <code>StyleBase</code> ensures that this method is invoked in the AWT
+ * thread only.
+ *
+ * @param observer profile system observer
+ * @param item the styled diagram item data
+ * @param element the styled diagram element representing the data item
+ * @param result the latest result calculated by
+ * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}
+ */
+ public void applyStyleForNode(EvaluationContext evaluationContext, INode node, Result result) {
+ }
+
+ public void applyStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item, Result value) {
+
+ final INode node = map.getNode(item);
+ if (node == null) {
+ evaluationContext.update();
+ // TODO: continue or return?
+ return;
+ }
+
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_APPLICATION)
+ System.out.println(StyleBase.this + ": applying style for item " + item + " and element " + node + " with result " + value);
+
+ applyStyleForNode(evaluationContext, node, value);
+
+ }
+
+ /**
+ * This method is invoked by
+ * {@link #cleanupStyleForNode(EvaluationContext, INode)} when the style is
+ * deactivated. It is invoked for each diagram element tracked by the style
+ * before deactivation.
+ *
+ * @param node a previously tracked and styled scene graph node
+ */
+ protected void cleanupStyleForNode(INode node) {
+ }
+
+ /**
+ * This method is invoked by
+ * {@link #cleanupStyleForItem(EvaluationContext, DataNodeMap, Object)} when the style is
+ * deactivated. It is invoked for each diagram element tracked by the style
+ * before deactivation.
+ * @param evaluationContext the context of this style evaluation
+ * @param node a previously tracked and styled scene graph node
+ */
+ protected void cleanupStyleForNode(EvaluationContext evaluationContext, INode node) {
+ cleanupStyleForNode(node);
+ }
+
+ protected void cleanupStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item) {
+
+ final INode node = map.getNode(item);
+ if (node != null) {
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
+ System.out.println(this + ".cleanupStyleForItem(" + item + " = " + node + ")");
+ cleanupStyleForNode(evaluationContext, node);
+ }
+
+ }
+
+
+ static class GroupListener<T> extends ObserverGroupListener {
+
+ private StyleBase<T> style;
+ private Session session;
+ private Resource runtimeDiagram;
+ private Resource entry;
+
+ GroupListener(Session session, Resource runtimeDiagram, Resource entry, StyleBase<T> style, Group group, Observer observer) {
+ super(style, group, observer);
+ this.style = style;
+ this.session = session;
+ this.runtimeDiagram = runtimeDiagram;
+ this.entry = entry;
+ }
+
+ @Override
+ public void add(final Resource item) {
+
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)
+ System.out.println(style + ": added to group " + group + ": " + item);
+
+ session.asyncRequest(
+ style.getStyleCalculationRequest(runtimeDiagram, entry, item),
+ style.getStyleResultListener(this, item, group, observer, runtimeDiagram)
+ );
+
+ super.add(item);
+ }
+ @Override
+ public void remove(Resource item) {
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)
+ System.out.println(style + ": removed from group " + group + ": " + item);
+
+ synchronized (style.removals) {
+ style.removals.add(item);
+ }
+
+ // TODO: do something here to dispose of ObserverGroupValueListeners?
+ super.remove(item);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.simantics.diagram.profile.Style#activate(org.simantics.db.RequestProcessor, org.simantics.db.Resource, org.simantics.db.layer0.variable.Variable, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)
+ */
+ @Override
+ public final void activate(RequestProcessor backend, final Resource runtimeDiagram, final Resource entry, final Group group, final EvaluationContext observer) {
+
+ ObserverGroupListener listener = getListener(runtimeDiagram, group);
+
+ if (listener == null || listener.isDisposed()) {
+
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
+ System.out.println("activate(" + runtimeDiagram + ", " + group + ", " + observer);
+
+ listener = new GroupListener<Result>(backend.getSession(), runtimeDiagram, entry, this, group, observer);
+
+ listeners.put(Pair.make(runtimeDiagram, group), listener);
+
+ group.trackItems(backend, runtimeDiagram, listener);
+
+ }
+
+ // Register this entry in the listener
+ listener.addEntry(entry);
+ }
+
+ /**
+ * Used to customize the identity given to graph requests made for this
+ * style. Default identity is getClass().
+ *
+ * @return identity object used in graph requests made by this style
+ */
+ protected Object getIdentity(Resource entry) {
+ return new Pair<Class<?>, Resource>(getClass(), entry);
+ }
+
+ /**
+ * @param configuration
+ * @param runtimeDiagram
+ * @param item
+ * @return
+ */
+ protected Read<Result> getStyleCalculationRequest(Resource runtimeDiagram, final Resource entry, Resource item) {
+ return new TernaryRead<Object, Resource, Resource, Result>(getIdentity(entry), runtimeDiagram, item) {
+ @Override
+ public Result perform(ReadGraph graph) throws DatabaseException {
+ boolean oldSynchronous = graph.getSynchronous();
+ try {
+ graph.setSynchronous(false);
+ Result result = calculateStyle(graph, parameter2, entry, parameter3);
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)
+ System.out.println(StyleBase.this + ": calculated style result for " + NameUtils.getSafeName(graph, parameter3, true) + ": " + result);
+ return result;
+ } finally {
+ graph.setSynchronous(oldSynchronous);
+ }
+ }
+ };
+ }
+
+ /**
+ * @param groupListener
+ * @param item
+ * @param group
+ * @param observer
+ * @param runtimeDiagram
+ * @return
+ */
+ protected Listener<Result> getStyleResultListener(ObserverGroupListener groupListener, final Resource item,
+ Group group, Observer observer, final Resource runtimeDiagram) {
+ return new ObserverGroupValueListener<Result>(groupListener, observer, group, item) {
+ @Override
+ public void execute(Result result) {
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)
+ System.out.println(StyleBase.this + ": style result changed for " + item + ": " + result);
+ styleResultChanged(observer, runtimeDiagram, item, result);
+ }
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see org.simantics.diagram.profile.Style#deactivate(org.simantics.db.RequestProcessor, org.simantics.db.Resource, org.simantics.db.layer0.variable.Variable, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)
+ */
+ @Override
+ public final void deactivate(Resource runtimeDiagram, Resource entry, Group group,
+ EvaluationContext observer) {
+
+ ObserverGroupListener listener = getListener(runtimeDiagram, group);
+ if (listener != null) {
+
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
+ System.out.println("deactivate(" + runtimeDiagram + ", " + group + ", " + observer);
+
+ IDiagram diagram = observer.getConstant(ProfileKeys.DIAGRAM);
+
+ listener.removeEntry(entry);
+ if (!listener.hasEntries()) {
+ listener.dispose();
+ listeners.remove(Pair.make(runtimeDiagram, group));
+ }
+
+ // This was too eager when multiple groups were tracked!
+ //values.clear();
+ if (diagram != null) {
+ cleanupItems(observer, diagram, listener.getItems().toArray());
+ diagram = null;
+ }
+ observer.update();
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.simantics.diagram.profile.Style#apply(org.simantics.g2d.diagram.IDiagram, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)
+ */
+ @Override
+ public final void apply(Resource entry, Group group, final EvaluationContext evaluationContext) {
+
+ ICanvasContext context = evaluationContext.getConstant(ProfileKeys.CANVAS);
+
+ assert context.getThreadAccess().currentThreadAccess();
+
+ ObserverGroupListener listener = getListener(evaluationContext.getResource(), group);
+ if (listener == null) {
+ System.out.println(this + "(" + getClass().getSimpleName() + ") had no listener for " + evaluationContext.getResource() + " " + group);
+ return;
+ }
+
+ final DataNodeMap map = evaluationContext.getConstant(ProfileKeys.NODE_MAP);
+
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_APPLICATION)
+ System.out.println(StyleBase.this + ": applying style for items: " + listener.getItems());
+
+ if (!removals.isEmpty()) {
+ Resource[] removed;
+ synchronized (removals) {
+ removed = removals.toArray(Resource.NONE);
+ removals.clear();
+ }
+ for (Resource item : removed) {
+ cleanupStyleForItem(evaluationContext, map, item);
+ }
+ }
+
+ for (Object item : listener.getItems()) {
+ Result value = values.get(new Tuple2(evaluationContext.getResource(), item));
+ applyStyleForItem(evaluationContext, map, item, value);
+ }
+
+ }
+
+ /**
+ * This is ran when this profile entry gets deactivated after being first
+ * active. It allows cleaning up scene graph left-overs for the listened set
+ * of items before deactivation. It will invoke
+ * {@link #cleanupStyleForElement(IDiagram, IElement)} for each diagram element observed
+ * before deactivation in the AWT thread. If the profile observer is
+ * disposed in between scheduling to AWT thread, the method will do nothing.
+ *
+ * @param observer the diagram profile observer
+ * @param diagram the diagram this profile system is working with
+ * @param items the diagram data items that need to be cleaned up
+ */
+ protected final void cleanupItems(final EvaluationContext evaluationContext, final IDiagram diagram, final Object[] items) {
+
+ ICanvasContext context = evaluationContext.getConstant(ProfileKeys.CANVAS);
+
+ context.getThreadAccess().asyncExec(new Runnable() {
+
+ @Override
+ public void run() {
+
+ if (evaluationContext.isDisposed())
+ return;
+
+ final DataNodeMap map = evaluationContext.getConstant(ProfileKeys.NODE_MAP);
+
+ if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
+ System.out.println(this + ".cleanupItems(" + evaluationContext + ", " + diagram + ", " + Arrays.toString(items));
+
+ for (Object item : items) {
+ cleanupStyleForItem(evaluationContext, map, item);
+ }
+ }
+ });
+ }
+
+ private ObserverGroupListener getListener(Resource runtime, Group group) {
+ return listeners.get(Pair.make(runtime, group));
+ }
+
+}