X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fprofile%2FStyleBase.java;h=fee1e8546d7e96e07bf13a21f53b98f26afac6c4;hp=888a6ea0ec14a16b450ce8de7b5dc61e7eeaea89;hb=HEAD;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/profile/StyleBase.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/profile/StyleBase.java index 888a6ea0e..fee1e8546 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/profile/StyleBase.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/profile/StyleBase.java @@ -1,483 +1,552 @@ -/******************************************************************************* - * 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: - * - * - *

- * 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 type of result objects for styled group items tracked by this - * style implementation, see - * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)} - */ -public abstract class StyleBase implements Style { - - protected final Map values = new ConcurrentHashMap(); - -// private Map listeners = new ConcurrentHashMap(); - - private Map, ObserverGroupListener> listeners = new HashMap, ObserverGroupListener>(); - - - private final List removals = new ArrayList(); - - /** - * For caching this simple base request that is done in every - * {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource)}. - */ - static class RuntimeDiagramVariableRequest extends UnaryRead { - 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. - * - *

- * 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.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). - * - *

- * StyleBase 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 extends ObserverGroupListener { - - private StyleBase style; - private Session session; - private Resource runtimeDiagram; - private Resource entry; - - GroupListener(Session session, Resource runtimeDiagram, Resource entry, StyleBase 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(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, Resource>(getClass(), entry); - } - - /** - * @param configuration - * @param runtimeDiagram - * @param item - * @return - */ - protected Read getStyleCalculationRequest(Resource runtimeDiagram, final Resource entry, Resource item) { - return new TernaryRead(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 getStyleResultListener(ObserverGroupListener groupListener, final Resource item, - Group group, Observer observer, final Resource runtimeDiagram) { - return new ObserverGroupValueListener(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) { - AWTThread.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)); - } - -} +/******************************************************************************* + * 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.Arrays; + +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.Hints; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.DataElementMap; +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.Tuple3; +import org.simantics.utils.datastructures.Pair; + +/** + * For most style implementations it should be enough to override the following + * methods: + *

+ * + *

+ * 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 type of result objects for styled group items tracked by this + * style implementation, see + * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)} + */ +public abstract class StyleBase implements Style { + + private Object identity; + private double priority; + + public StyleBase(Object identity) { + this.identity = identity; + } + + public StyleBase() { + this.identity = getClass(); + } + + @SuppressWarnings("unchecked") + protected T getIdentity() { + return (T)identity; + } + + public void setPriority(double priority) { + this.priority = priority; + } + + public double getPriority() { + return priority; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((identity == null) ? 0 : identity.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StyleBase other = (StyleBase) obj; + if (identity == null) { + if (other.identity != null) + return false; + } else if (!identity.equals(other.identity)) + return false; + return true; + } + + protected Resource getResource() { + return getIdentity(); + } + + /** + * For caching this simple base request that is done in every + * {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource)}. + */ + static class RuntimeDiagramVariableRequest extends UnaryRead { + 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. + * + *

+ * 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.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) + StyleBaseData.getInstance().removeValue(new Tuple3(this, runtimeDiagram, object)); + else + StyleBaseData.getInstance().putValue(new Tuple3(this, runtimeDiagram, object), result); + observer.update(this, object); + } + + /** + * 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). + * + *

+ * StyleBase 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(this, item); + // 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 extends ObserverGroupListener { + + private StyleBase style; + private Session session; + private Resource runtimeDiagram; + private Resource entry; + + GroupListener(Session session, Resource runtimeDiagram, Resource entry, StyleBase 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); + + StyleBaseData.getInstance().removeItem(style, 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) throws DatabaseException { + + 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(backend.getSession(), runtimeDiagram, entry, this, group, observer); + + StyleBaseData.getInstance().putListener(new Tuple3(this, 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, Resource>(getClass(), entry); + } + + /** + * @param configuration + * @param runtimeDiagram + * @param item + * @return + */ + protected Read getStyleCalculationRequest(Resource runtimeDiagram, final Resource entry, Resource item) { + return new TernaryRead(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 getStyleResultListener(ObserverGroupListener groupListener, final Resource item, + Group group, Observer observer, final Resource runtimeDiagram) { + return new ObserverGroupValueListener(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(); + StyleBaseData.getInstance().removeListener(new Tuple3(this, 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(); TODO: Check if this is required! + } + + } + + /* (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()); + + + StyleBaseData data = StyleBaseData.getInstance(); + + data.applyRemovals(evaluationContext, this); + + IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM); + assert diagram != null; + DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class); + + for (Object item : listener.getItems()) { + Result value = data.getValue(new Tuple3(this, evaluationContext.getResource(), item)); + applyStyleForItem(evaluationContext, map, item, value); + + IElement element = emap.getElement(diagram, item); + if (element != null) + element.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY); + } + + } + + @Override + public final void apply2(Object item, final EvaluationContext evaluationContext) { + final DataNodeMap map = evaluationContext.getConstant(ProfileKeys.NODE_MAP); + + StyleBaseData data = StyleBaseData.getInstance(); + + data.applyRemovals(evaluationContext, this); + + Result value = data.getValue(new Tuple3(this, evaluationContext.getResource(), item)); + applyStyleForItem(evaluationContext, map, item, value); + + IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM); + assert diagram != null; + DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class); + IElement element = emap.getElement(diagram, item); + if (element != null) + element.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY); + } + + /** + * 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)); + + IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM); + assert diagram != null; + DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class); + + for (Object item : items) { + cleanupStyleForItem(evaluationContext, map, item); + + IElement element = emap.getElement(diagram, item); + if (element != null) + element.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY); + } + } + }); + } + + private ObserverGroupListener getListener(Resource runtime, Group group) { + return StyleBaseData.getInstance().getListener(new Tuple3(this, runtime, group)); + } + +}