]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/profile/ShowRelatedElements.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / profile / ShowRelatedElements.java
index a4e003c5f579d5d2532e10b9de5d758df8fb4a84..cd4786088afc2ad4358e5b51eb17be19aca7d609 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2011 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.awt.BasicStroke;\r
-import java.awt.Color;\r
-import java.awt.Stroke;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Line2D;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.List;\r
-\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Statement;\r
-import org.simantics.db.common.utils.OrderedSetUtils;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.variable.Variable;\r
-import org.simantics.diagram.content.ConnectionUtil;\r
-import org.simantics.diagram.flag.FlagUtil;\r
-import org.simantics.diagram.stubs.DiagramResource;\r
-import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
-import org.simantics.g2d.connection.handler.ConnectionHandler;\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.handler.DataElementMap;\r
-import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;\r
-import org.simantics.g2d.diagram.handler.Relationship;\r
-import org.simantics.g2d.diagram.handler.Topology.Connection;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.BendsHandler;\r
-import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
-import org.simantics.g2d.utils.GeometryUtils;\r
-import org.simantics.modeling.ModelingResources;\r
-import org.simantics.scenegraph.INode;\r
-import org.simantics.scenegraph.g2d.G2DParentNode;\r
-import org.simantics.scenegraph.g2d.nodes.RelationshipNode2;\r
-import org.simantics.scenegraph.profile.DataNodeMap;\r
-import org.simantics.scenegraph.profile.EvaluationContext;\r
-import org.simantics.scenegraph.profile.common.ProfileVariables;\r
-import org.simantics.utils.datastructures.Pair;\r
-import org.simantics.utils.datastructures.map.Tuple;\r
-\r
-/**\r
- * @author Tuukka Lehtonen\r
- */\r
-public class ShowRelatedElements extends StyleBase<Object> {\r
-\r
-    protected Collection<Connection> terminalConnections = new ArrayList<Connection>(4);\r
-\r
-    protected final BasicStroke highlightStroke = new BasicStroke(1.0f,\r
-            BasicStroke.CAP_SQUARE, BasicStroke.CAP_SQUARE, 10.0f,\r
-            new float[] { 4.0f, 6.0f }, 0);\r
-\r
-    protected Line2D line = new Line2D.Double();\r
-\r
-    /**\r
-     * One possible result of\r
-     * {@link ShowRelatedElements#calculateStyle(ReadGraph, Resource, Resource, Variable)}\r
-     */\r
-    public static class Relation extends Tuple {\r
-        public Relation(String relation,\r
-                Resource element, AffineTransform elementTransform,\r
-                Resource otherElement, AffineTransform otherElementTransform)\r
-        {\r
-            super(relation, element, elementTransform, otherElement, otherElementTransform);\r
-        }\r
-        public String getRelation() {\r
-            return (String) getField(0);\r
-        }\r
-        public Resource getElement() {\r
-            return (Resource) getField(1);\r
-        }\r
-        public AffineTransform getTransform() {\r
-            return (AffineTransform) getField(2);\r
-        }\r
-        public Resource getOtherElement() {\r
-            return (Resource) getField(3);\r
-        }\r
-        public AffineTransform getOtherTransform() {\r
-            return (AffineTransform) getField(4);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Calculates a result that is contains the styled element, its parent and\r
-     * transforms of both. This takes care of the style updating the visuals\r
-     * properly every time the related components move.\r
-     */\r
-    @Override\r
-    public Object calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)\r
-    throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-\r
-        if (graph.isInstanceOf(element, DIA.Monitor)) {\r
-            return calculateFromComponent(graph, runtimeDiagram, element, DIA.HasMonitorComponent);\r
-        }\r
-        if (graph.isInstanceOf(element, DIA.Flag)) {\r
-            return calculateFromFlag(graph, element);\r
-        }\r
-\r
-        // Just return anything, no relationship to visualize.\r
-        return Boolean.TRUE;\r
-    }\r
-\r
-    protected Object calculateFromFlag(ReadGraph graph, Resource flag) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-\r
-        Resource relatedFlag = FlagUtil.getPossibleCounterpart(graph, flag);\r
-        if (relatedFlag != null) {\r
-            Collection<Resource> flagDiagrams = OrderedSetUtils.getOwnerLists(graph, flag, DIA.Diagram);\r
-            Collection<Resource> relatedDiagrams = OrderedSetUtils.getOwnerLists(graph, relatedFlag, DIA.Diagram);\r
-            if (!Collections.disjoint(flagDiagrams, relatedDiagrams)) {\r
-                AffineTransform flagAt = DiagramGraphUtil.getTransform(graph, flag);\r
-                AffineTransform relatedFlagAt = DiagramGraphUtil.getTransform(graph, relatedFlag);\r
-                return new Relation(Relationship.RELATED_TO.toString(), flag, flagAt, relatedFlag, relatedFlagAt);\r
-            }\r
-        }\r
-\r
-        return Boolean.TRUE;\r
-    }\r
-\r
-    protected Object calculateFromComponent(ReadGraph graph, Resource runtime, Resource element, Resource elementToComponentRelation) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        ModelingResources MOD = ModelingResources.getInstance(graph);\r
-\r
-        Resource parentComponent = graph.getPossibleObject(element, elementToComponentRelation);\r
-        if (parentComponent != null) {\r
-            Resource parentElement = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);\r
-            if (parentElement != null) {\r
-                AffineTransform at = DiagramGraphUtil.getTransform(graph, element);\r
-                if (graph.isInstanceOf(parentElement, DIA.Connection))\r
-                    return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement,\r
-                            getConnectionOutputNodePosition(graph, runtime, parentElement));\r
-\r
-                AffineTransform parentAt = DiagramGraphUtil.getTransform(graph, parentElement);\r
-                return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement, parentAt);\r
-            }\r
-        }\r
-\r
-        return Boolean.TRUE;\r
-    }\r
-\r
-    protected AffineTransform getConnectionOutputNodePosition(ReadGraph graph, Resource runtime, Resource connection) throws DatabaseException {\r
-        Statement toTail = ConnectionUtil.getConnectionTailNodeStatement(graph, connection);\r
-        if (toTail == null)\r
-            return null;\r
-\r
-        AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, toTail.getObject());\r
-\r
-        Resource connectionPoint = graph.getPossibleInverse(toTail.getPredicate());\r
-        if (connectionPoint == null)\r
-            return at;\r
-\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        for (Resource terminal : graph.getObjects(connectionPoint, DIA.HasConnectionPoint_Inverse)) {\r
-            AffineTransform terminalAt = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, terminal);\r
-            at.concatenate(terminalAt);\r
-        }\r
-\r
-        return at;\r
-    }\r
-\r
-    @Override\r
-    public void applyStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item, Object value) {\r
-        INode node = map.getNode(item);\r
-        if (node == null) {\r
-            evaluationContext.update();\r
-            return;\r
-        }\r
-\r
-        if (!(value instanceof Relation)) {\r
-            cleanupStyleForNode(node);\r
-            return;\r
-        }\r
-\r
-        IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM);\r
-\r
-        Relation r = (Relation) value;\r
-\r
-        DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
-        IElement element = emap.getElement(diagram, item);\r
-        IElement otherElement = emap.getElement(diagram, r.getOtherElement());\r
-        if (otherElement != null)\r
-            addCorrespondence(r, (G2DParentNode) node, r.getRelation(), element, otherElement, highlightStroke, Color.GRAY);\r
-    }\r
-\r
-    protected void addCorrespondence(Relation r, G2DParentNode p, String id, IElement from, IElement to, Stroke lineStroke, Color lineColor) {\r
-        Pair<Point2D, Rectangle2D> p1 = toPoint(r.getTransform(), from);\r
-        Pair<Point2D, Rectangle2D> p2 = toPoint(r.getOtherTransform(), to);\r
-        if (p1 != null && p2 != null)\r
-            addCorrespondence(p, id, p1, p2, lineStroke, lineColor);\r
-    }\r
-\r
-    /**\r
-     * @param parentTransform\r
-     * @param e \r
-     * @return origin and bounding box of the specified element in canvas\r
-     *         coordinate system\r
-     */\r
-    protected Pair<Point2D, Rectangle2D> toPoint(AffineTransform parentTransform, IElement e) {\r
-        if (PickFilter.FILTER_CONNECTIONS.accept(e)) {\r
-            // For connections select the first found output terminal position as\r
-            // the correspondence visualization point.\r
-\r
-            ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);\r
-            terminalConnections.clear();\r
-            ch.getTerminalConnections(e, terminalConnections);\r
-            for (Connection c : terminalConnections) {\r
-                if (c.end != EdgeEnd.Begin)\r
-                    continue;\r
-\r
-                BendsHandler bh = c.edge.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-                if (bh != null) {\r
-                    List<Point2D> points = new ArrayList<Point2D>();\r
-                    GeometryUtils.getPoints(bh.getPath(c.edge), points);\r
-                    if (!points.isEmpty())\r
-                        return Pair.make(points.get(0), null);\r
-                } else {\r
-                    AffineTransform at = parentTransform;\r
-                    Point2D point = new Point2D.Double(at.getTranslateX(), at.getTranslateY());\r
-                    return Pair.make(point, null);\r
-                }\r
-            }\r
-\r
-            // Fall back to the default logic if connection output terminal\r
-            // search fails.\r
-        }\r
-\r
-        // Using element origin prevents the relationship rubber band target\r
-        // from changing every time the content of the node changes, e.g. the\r
-        // text of a monitor.\r
-        AffineTransform at = ElementUtils.getTransform(e);\r
-        Point2D p = new Point2D.Double(at.getTranslateX(), at.getTranslateY());\r
-        Rectangle2D r = ElementUtils.getElementBoundsOnDiagram(e).getBounds2D();\r
-        return Pair.make(p, r);\r
-    }\r
-\r
-    protected void addCorrespondence(G2DParentNode p, String id,\r
-            Pair<Point2D, Rectangle2D> p1,\r
-            Pair<Point2D, Rectangle2D> p2,\r
-            Stroke lineStroke, Color lineColor)\r
-    {\r
-        RelationshipNode2 node = p.getOrCreateNode(id, RelationshipNode2.class);\r
-        node.setZIndex(-10);\r
-\r
-        if (p1.second != null) {\r
-            line.setLine(p1.first, p2.first);\r
-            if (LineUtilities.clipLine(line, p1.second))\r
-                p1.first.setLocation(line.getX2(), line.getY2());\r
-        }\r
-        if (p2.second != null) {\r
-            line.setLine(p1.first, p2.first);\r
-            if (LineUtilities.clipLine(line, p2.second))\r
-                p2.first.setLocation(line.getX1(), line.getY1());\r
-        }\r
-\r
-        node.init(lineStroke, lineColor, p1.first, p2.first);\r
-    }\r
-\r
-    @Override\r
-    protected void cleanupStyleForNode(INode node) {\r
-        ProfileVariables.denyChild(node, "", Relationship.CHILD_OF.toString()); \r
-        //ProfileVariables.denyChild(node, "", Relationship.PARENT_OF.toString()); \r
-        ProfileVariables.denyChild(node, "", Relationship.RELATED_TO.toString()); \r
-    }\r
-\r
-    @Override\r
-    public String toString() {\r
-        return "show related elements";\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2011 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.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Statement;
+import org.simantics.db.common.utils.OrderedSetUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.diagram.content.ConnectionUtil;
+import org.simantics.diagram.flag.FlagUtil;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
+import org.simantics.g2d.connection.handler.ConnectionHandler;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.DataElementMap;
+import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;
+import org.simantics.g2d.diagram.handler.Relationship;
+import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.element.ElementUtils;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.BendsHandler;
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
+import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.nodes.RelationshipNode2;
+import org.simantics.scenegraph.profile.DataNodeMap;
+import org.simantics.scenegraph.profile.EvaluationContext;
+import org.simantics.scenegraph.profile.common.ProfileVariables;
+import org.simantics.utils.datastructures.Pair;
+import org.simantics.utils.datastructures.map.Tuple;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class ShowRelatedElements extends StyleBase<Object> {
+
+    protected Collection<Connection> terminalConnections = new ArrayList<Connection>(4);
+
+    protected final BasicStroke highlightStroke = new BasicStroke(1.0f,
+            BasicStroke.CAP_SQUARE, BasicStroke.CAP_SQUARE, 10.0f,
+            new float[] { 4.0f, 6.0f }, 0);
+
+    protected Line2D line = new Line2D.Double();
+
+    /**
+     * One possible result of
+     * {@link ShowRelatedElements#calculateStyle(ReadGraph, Resource, Resource, Variable)}
+     */
+    public static class Relation extends Tuple {
+        public Relation(String relation,
+                Resource element, AffineTransform elementTransform,
+                Resource otherElement, AffineTransform otherElementTransform)
+        {
+            super(relation, element, elementTransform, otherElement, otherElementTransform);
+        }
+        public String getRelation() {
+            return (String) getField(0);
+        }
+        public Resource getElement() {
+            return (Resource) getField(1);
+        }
+        public AffineTransform getTransform() {
+            return (AffineTransform) getField(2);
+        }
+        public Resource getOtherElement() {
+            return (Resource) getField(3);
+        }
+        public AffineTransform getOtherTransform() {
+            return (AffineTransform) getField(4);
+        }
+    }
+
+    /**
+     * Calculates a result that is contains the styled element, its parent and
+     * transforms of both. This takes care of the style updating the visuals
+     * properly every time the related components move.
+     */
+    @Override
+    public Object calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)
+    throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+
+        if (graph.isInstanceOf(element, DIA.Monitor)) {
+            return calculateFromComponent(graph, runtimeDiagram, element, DIA.HasMonitorComponent);
+        }
+        if (graph.isInstanceOf(element, DIA.Flag)) {
+            return calculateFromFlag(graph, element);
+        }
+
+        // Just return anything, no relationship to visualize.
+        return Boolean.TRUE;
+    }
+
+    protected Object calculateFromFlag(ReadGraph graph, Resource flag) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+
+        Resource relatedFlag = FlagUtil.getPossibleCounterpart(graph, flag);
+        if (relatedFlag != null) {
+            Collection<Resource> flagDiagrams = OrderedSetUtils.getOwnerLists(graph, flag, DIA.Diagram);
+            Collection<Resource> relatedDiagrams = OrderedSetUtils.getOwnerLists(graph, relatedFlag, DIA.Diagram);
+            if (!Collections.disjoint(flagDiagrams, relatedDiagrams)) {
+                AffineTransform flagAt = DiagramGraphUtil.getTransform(graph, flag);
+                AffineTransform relatedFlagAt = DiagramGraphUtil.getTransform(graph, relatedFlag);
+                return new Relation(Relationship.RELATED_TO.toString(), flag, flagAt, relatedFlag, relatedFlagAt);
+            }
+        }
+
+        return Boolean.TRUE;
+    }
+
+    protected Object calculateFromComponent(ReadGraph graph, Resource runtime, Resource element, Resource elementToComponentRelation) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+        ModelingResources MOD = ModelingResources.getInstance(graph);
+
+        Resource parentComponent = graph.getPossibleObject(element, elementToComponentRelation);
+        if (parentComponent != null) {
+            Resource parentElement = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);
+            if (parentElement != null) {
+                AffineTransform at = DiagramGraphUtil.getTransform(graph, element);
+                if (graph.isInstanceOf(parentElement, DIA.Connection))
+                    return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement,
+                            getConnectionOutputNodePosition(graph, runtime, parentElement));
+
+                AffineTransform parentAt = DiagramGraphUtil.getTransform(graph, parentElement);
+                return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement, parentAt);
+            }
+        }
+
+        return Boolean.TRUE;
+    }
+
+    protected AffineTransform getConnectionOutputNodePosition(ReadGraph graph, Resource runtime, Resource connection) throws DatabaseException {
+        Statement toTail = ConnectionUtil.getConnectionTailNodeStatement(graph, connection);
+        if (toTail == null)
+            return null;
+
+        AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, toTail.getObject());
+
+        Resource connectionPoint = graph.getPossibleInverse(toTail.getPredicate());
+        if (connectionPoint == null)
+            return at;
+
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+        for (Resource terminal : graph.getObjects(connectionPoint, DIA.HasConnectionPoint_Inverse)) {
+            AffineTransform terminalAt = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, terminal);
+            at.concatenate(terminalAt);
+        }
+
+        return at;
+    }
+
+    @Override
+    public void applyStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item, Object value) {
+        INode node = map.getNode(item);
+        if (node == null) {
+            evaluationContext.update(this, item);
+            return;
+        }
+
+        if (!(value instanceof Relation)) {
+            cleanupStyleForNode(node);
+            return;
+        }
+
+        IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM);
+
+        Relation r = (Relation) value;
+
+        DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
+        IElement element = emap.getElement(diagram, item);
+        IElement otherElement = emap.getElement(diagram, r.getOtherElement());
+        if (otherElement != null)
+            addCorrespondence(r, (G2DParentNode) node, r.getRelation(), element, otherElement, highlightStroke, Color.GRAY);
+    }
+
+    protected void addCorrespondence(Relation r, G2DParentNode p, String id, IElement from, IElement to, Stroke lineStroke, Color lineColor) {
+        Pair<Point2D, Rectangle2D> p1 = toPoint(r.getTransform(), from);
+        Pair<Point2D, Rectangle2D> p2 = toPoint(r.getOtherTransform(), to);
+        if (p1 != null && p2 != null)
+            addCorrespondence(p, id, p1, p2, lineStroke, lineColor);
+    }
+
+    /**
+     * @param parentTransform
+     * @param e 
+     * @return origin and bounding box of the specified element in canvas
+     *         coordinate system
+     */
+    protected Pair<Point2D, Rectangle2D> toPoint(AffineTransform parentTransform, IElement e) {
+        if (PickFilter.FILTER_CONNECTIONS.accept(e)) {
+            // For connections select the first found output terminal position as
+            // the correspondence visualization point.
+
+            ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);
+            terminalConnections.clear();
+            ch.getTerminalConnections(e, terminalConnections);
+            for (Connection c : terminalConnections) {
+                if (c.end != EdgeEnd.Begin)
+                    continue;
+
+                BendsHandler bh = c.edge.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+                if (bh != null) {
+                    List<Point2D> points = new ArrayList<Point2D>();
+                    GeometryUtils.getPoints(bh.getPath(c.edge), points);
+                    if (!points.isEmpty())
+                        return Pair.make(points.get(0), null);
+                } else {
+                    AffineTransform at = parentTransform;
+                    Point2D point = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
+                    return Pair.make(point, null);
+                }
+            }
+
+            // Fall back to the default logic if connection output terminal
+            // search fails.
+        }
+
+        // Using element origin prevents the relationship rubber band target
+        // from changing every time the content of the node changes, e.g. the
+        // text of a monitor.
+        AffineTransform at = ElementUtils.getTransform(e);
+        Point2D p = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
+        Rectangle2D r = ElementUtils.getElementBoundsOnDiagram(e).getBounds2D();
+        return Pair.make(p, r);
+    }
+
+    protected void addCorrespondence(G2DParentNode p, String id,
+            Pair<Point2D, Rectangle2D> p1,
+            Pair<Point2D, Rectangle2D> p2,
+            Stroke lineStroke, Color lineColor)
+    {
+        RelationshipNode2 node = p.getOrCreateNode(id, RelationshipNode2.class);
+        node.setZIndex(-10);
+
+        if (p1.second != null) {
+            line.setLine(p1.first, p2.first);
+            if (LineUtilities.clipLine(line, p1.second))
+                p1.first.setLocation(line.getX2(), line.getY2());
+        }
+        if (p2.second != null) {
+            line.setLine(p1.first, p2.first);
+            if (LineUtilities.clipLine(line, p2.second))
+                p2.first.setLocation(line.getX1(), line.getY1());
+        }
+
+        node.init(lineStroke, lineColor, p1.first, p2.first);
+    }
+
+    @Override
+    protected void cleanupStyleForNode(INode node) {
+        ProfileVariables.denyChild(node, "", Relationship.CHILD_OF.toString()); 
+        //ProfileVariables.denyChild(node, "", Relationship.PARENT_OF.toString()); 
+        ProfileVariables.denyChild(node, "", Relationship.RELATED_TO.toString()); 
+    }
+
+    @Override
+    public String toString() {
+        return "show related elements";
+    }
+
+}