]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Refactored RouteGraphConnectionClassFactory code into RouteGraphUtils 64/164/1
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Wed, 23 Nov 2016 13:00:55 +0000 (15:00 +0200)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Wed, 23 Nov 2016 13:00:55 +0000 (15:00 +0200)
This concludes a refactoring started over 4 years ago.

refs #3832

Change-Id: I8832696c12038c74d0e28a7f0851de142d947a48

bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphConnectionClassFactory.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java

index 1b39ef87ba5b2c55486bc15c3f020f7fbbd7b2ca..c4594ff51674981bc97d90641d712fb3300f9a5a 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * Copyright (c) 2007, 2016 Association for Decentralized Information Management\r
  * in 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
@@ -8,56 +8,29 @@
  *\r
  * Contributors:\r
  *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *     Semantum Oy - refactoring\r
  *******************************************************************************/\r
 package org.simantics.diagram.adapter;\r
 \r
-import gnu.trove.map.hash.THashMap;\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-import java.awt.BasicStroke;\r
-import java.awt.Color;\r
-import java.awt.Shape;\r
-import java.awt.Stroke;\r
-import java.awt.geom.AffineTransform;\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.Map;\r
 import java.util.Set;\r
 \r
-import org.simantics.databoard.Bindings;\r
 import org.simantics.db.AsyncReadGraph;\r
 import org.simantics.db.ReadGraph;\r
 import org.simantics.db.Resource;\r
 import org.simantics.db.Session;\r
-import org.simantics.db.Statement;\r
-import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
-import org.simantics.db.common.request.ResourceRead2;\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.procedure.AsyncProcedure;\r
-import org.simantics.diagram.connection.ConnectionVisuals;\r
+import org.simantics.diagram.adapter.RouteGraphUtils.BackendConnection;\r
 import org.simantics.diagram.connection.RouteGraph;\r
 import org.simantics.diagram.connection.RouteGraphConnectionClass;\r
-import org.simantics.diagram.connection.RouteLine;\r
-import org.simantics.diagram.connection.RouteNode;\r
-import org.simantics.diagram.connection.RouteTerminal;\r
-import org.simantics.diagram.connection.rendering.BasicConnectionStyle;\r
 import org.simantics.diagram.connection.rendering.ConnectionStyle;\r
 import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;\r
-import org.simantics.diagram.connection.rendering.arrows.ArrowLineEndStyle;\r
 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;\r
-import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle;\r
-import org.simantics.diagram.content.EdgeResource;\r
 import org.simantics.diagram.content.ResourceTerminal;\r
-import org.simantics.diagram.content.TerminalMap;\r
-import org.simantics.diagram.query.DiagramRequests;\r
 import org.simantics.diagram.stubs.DiagramResource;\r
-import org.simantics.diagram.stubs.G2DResource;\r
-import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
-import org.simantics.diagram.synchronization.graph.RouteGraphConnection;\r
 import org.simantics.diagram.ui.DiagramModelHints;\r
 import org.simantics.g2d.canvas.ICanvasContext;\r
 import org.simantics.g2d.connection.ConnectionEntity;\r
@@ -67,23 +40,19 @@ import org.simantics.g2d.diagram.handler.Topology.Connection;
 import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
 import org.simantics.g2d.element.ElementClass;\r
 import org.simantics.g2d.element.ElementHints;\r
-import org.simantics.g2d.element.ElementUtils;\r
 import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
 import org.simantics.g2d.element.handler.TerminalTopology;\r
 import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;\r
-import org.simantics.g2d.elementclass.FlagClass.Type;\r
 import org.simantics.g2d.utils.TopologicalSelectionExpander;\r
 import org.simantics.layer0.Layer0;\r
 import org.simantics.modeling.ModelingResources;\r
 import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;\r
 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;\r
-import org.simantics.scenegraph.utils.GeometryUtils;\r
 import org.simantics.structural.stubs.StructuralResource2;\r
-import org.simantics.structural2.modelingRules.CPTerminal;\r
-import org.simantics.structural2.modelingRules.IAttachmentRelationMap;\r
 import org.simantics.structural2.modelingRules.IModelingRules;\r
 \r
+import gnu.trove.set.hash.THashSet;\r
+\r
 /**\r
  * An element class for single connection entity elements. A connection entity\r
  * consists of connection edge segments and branch points as its children.\r
@@ -92,12 +61,10 @@ import org.simantics.structural2.modelingRules.IModelingRules;
  */\r
 public class RouteGraphConnectionClassFactory extends SyncElementFactory {\r
 \r
-    private static final boolean       DEBUG = false;\r
-\r
     public static final ElementClass   CLASS = RouteGraphConnectionClass.CLASS;\r
 \r
-    public static final ILineEndStyle HEAD  = new ArrowLineEndStyle("fill 2 1 0");\r
-    public static final ILineEndStyle TAIL  = PlainLineEndStyle.INSTANCE;\r
+    public static final ILineEndStyle  HEAD  = RouteGraphUtils.HEAD;\r
+    public static final ILineEndStyle  TAIL  = RouteGraphUtils.TAIL;\r
 \r
     protected Layer0                   L0;\r
     protected DiagramResource          DIA;\r
@@ -129,256 +96,12 @@ public class RouteGraphConnectionClassFactory extends SyncElementFactory {
         IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);\r
         Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);\r
 \r
-        RouteGraph rg = new RouteGraph();\r
-\r
-        // Default capacity should be enough for common cases.\r
-        Set<EdgeResource> links = new THashSet<EdgeResource>();\r
-        Map<Object, RouteNode> nodeByData = new THashMap<Object, RouteNode>();\r
-\r
-        // Load all route graph interior RouteNodes: route lines and points\r
-        for (Resource interiorNode : graph.getObjects(connection, DIA.HasInteriorRouteNode)) {\r
-            if (graph.isInstanceOf(interiorNode, DIA.RouteLine)) {\r
-                Boolean isHorizontal = graph.getRelatedValue(interiorNode, DIA.IsHorizontal, Bindings.BOOLEAN);\r
-                Double position = graph.getRelatedValue(interiorNode, DIA.HasPosition, Bindings.DOUBLE);\r
-                RouteLine line = rg.addLine(isHorizontal, position);\r
-                line.setData( RouteGraphConnection.serialize(graph, interiorNode) );\r
-\r
-                nodeByData.put( interiorNode, line );\r
-\r
-                for (Resource connectedTo : graph.getObjects(interiorNode, DIA.AreConnected)) {\r
-                    links.add( new EdgeResource(interiorNode, connectedTo) );\r
-                }\r
-            } else if (graph.isInstanceOf(interiorNode, DIA.RoutePoint)) {\r
-                // Not supported yet. Ignore.\r
-            }\r
-        }\r
-\r
-        Rectangle2D bounds = new Rectangle2D.Double();\r
-        Map<Resource, Resource> connectorToModeledAttachment = null;\r
-\r
-        // Primarily the loader will believe what modeling rules say about\r
-        // connector attachment relations.\r
-        // \r
-        // If modeling rules decide nothing, then we simply believe what the\r
-        // the attachment relations in the graph say.\r
-        // \r
-        // Special case 1: connection with two (2) terminals\r
-        // If the attachment of one of two terminals is decided by modeling\r
-        // rules, the other attachment shall be the opposite of the decided\r
-        // attachment (see forcedAttachmentRelation below).\r
-        // \r
-        // Special case 2: connected to a flag\r
-        // If the attached element is a flag and modeling rules say nothing\r
-        // about it, believe the direction stated by the flag type.\r
-\r
-        Collection<Statement> toConnectorStatements = graph.getStatements(connection, DIA.HasConnector);\r
-        int terminalCount = 0;\r
-\r
-        // See if modeling rules judge any of the connection terminal attachments.\r
-        if (modelingRules != null) {\r
-            for (Statement toConnector : toConnectorStatements) {\r
-                Resource connector = toConnector.getObject();\r
-\r
-                Statement terminalStm = findTerminalStatement(graph, connection, connector);\r
-                if (terminalStm == null)\r
-                    // Ignore broken connector: attached to the connection but not to any terminal.\r
-                    continue;\r
-\r
-                Resource terminalElement = terminalStm.getObject();\r
-                Resource connectionRelation = graph.getPossibleInverse(terminalStm.getPredicate());\r
-                if (connectionRelation == null)\r
-                    continue;\r
-\r
-                ++terminalCount;\r
-\r
-                IAttachmentRelationMap map = modelingRules.getAttachmentRelations(graph, connection);\r
-                Resource attachment = map.get(graph, new CPTerminal(terminalElement, connectionRelation));\r
-                if (attachment != null) {\r
-                    // Primary: believe modeling rules\r
-                    if (connectorToModeledAttachment == null)\r
-                        connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());\r
-                    connectorToModeledAttachment.put(connector, attachment);\r
-                    if (DEBUG)\r
-                        System.out.println("modeling rules decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");\r
-                } else if (graph.isInstanceOf(terminalElement, DIA.Flag)) {\r
-                    // Secondary: believe flag type\r
-                    attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules);\r
-                    if (attachment != null) {\r
-                        if (connectorToModeledAttachment == null)\r
-                            connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());\r
-                        connectorToModeledAttachment.put(connector, attachment);\r
-                        if (DEBUG)\r
-                            System.out.println("flag type decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        if (connectorToModeledAttachment == null)\r
-            connectorToModeledAttachment = Collections.emptyMap();\r
-\r
-        Resource forcedAttachmentRelation = null;\r
-        if (terminalCount == 2 && connectorToModeledAttachment.size() == 1) {\r
-            forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next());\r
-            if (DEBUG)\r
-                System.out.println("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation));\r
-        }\r
-\r
-        Resource connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); \r
-\r
-        // Needed to support ConnectionEntity#getTerminalConnections\r
-        Set<BackendConnection> backendConnections = new THashSet<BackendConnection>(toConnectorStatements.size(), 0.75f);\r
-\r
-        // Load all node terminal connections as RouteTerminals\r
-        for (Statement toConnector : toConnectorStatements) {\r
-            Resource connector = toConnector.getObject();\r
-            Resource attachmentRelation = toConnector.getPredicate();\r
-            if (DEBUG)\r
-                System.out.println("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
-\r
-            Statement terminalStm = findTerminalStatement(graph, connection, connector);\r
-            if (terminalStm == null)\r
-                // Ignore broken connector: attached to the connection but not to any terminal.\r
-                continue;\r
-\r
-            Resource terminalElement = terminalStm.getObject();\r
-            Resource terminalElementType = graph.getPossibleType(terminalElement, DIA.Element);\r
-            if (terminalElementType == null)\r
-                // Ignore non-element terminal elements\r
-                continue;\r
-\r
-            Resource connectionRelation = graph.getPossibleInverse(terminalStm.getPredicate());\r
-            if (connectionRelation == null)\r
-                continue;\r
-\r
-            // Discover node and terminal this connector is connected to.\r
-            TerminalMap terminals = graph.syncRequest(DiagramRequests.elementTypeTerminals(terminalElementType),\r
-                    TransientCacheListener.<TerminalMap>instance());\r
-            Resource terminal = terminals.getTerminal(connectionRelation);\r
-            if (terminal == null) {\r
-                System.err.println(getClass().getSimpleName()\r
-                        + ": Could not find terminal for connection point "\r
-                        + NameUtils.getSafeName(graph, connectionRelation, true)\r
-                        + " in element "\r
-                        + NameUtils.getSafeName(graph, terminalElement, true)); \r
-                continue;\r
-            }\r
-\r
-            double[] position = graph.getRelatedValue(connector, DIA.HasRelativeLocation, Bindings.DOUBLE_ARRAY);\r
-            if (position.length != 2)\r
-                position = new double[] { 0, 0 };\r
-\r
-            if (DEBUG) {\r
-                System.out.println("terminalStm: " + NameUtils.toString(graph, terminalStm));\r
-                System.out.println("terminal: " + graph.getURI(terminalStm.getPredicate()));\r
-            }\r
-            AffineTransform terminalElementTr = DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement);\r
-            if (DEBUG)\r
-                System.out.println("terminalElementTr: " + terminalElementTr);\r
-\r
-            double x = terminalElementTr.getTranslateX();\r
-            double y = terminalElementTr.getTranslateY();\r
-            double minx = x-1, miny = y-1, maxx = x+1, maxy = y+1;\r
-            int direction = 0x0;\r
-\r
-            // Use modelingRules to ascertain the proper attachmentRelation\r
-            // for this terminal connection, if available.\r
-            Resource att = connectorToModeledAttachment.get(connector);\r
-            if (att != null) {\r
-                attachmentRelation = att;\r
-                if (DEBUG)\r
-                    System.out.println("modeling rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
-            } else if (forcedAttachmentRelation != null) {\r
-                attachmentRelation = forcedAttachmentRelation;\r
-                if (DEBUG)\r
-                    System.out.println("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
-            }\r
-            if (DEBUG)\r
-                System.out.println("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
-\r
-            // Get element bounds to decide allowed terminal direction(s)\r
-            IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));\r
-            ElementUtils.getElementBounds(te, bounds);\r
-            {\r
-                Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalElementTr);\r
-                bounds.setFrame(shp.getBounds2D());\r
-            }\r
-\r
-            // Expand bounds by 2mm to make the connections enter the terminals\r
-            // at a straight angle and from a distance instead of coming in\r
-            // "horizontally".\r
-            GeometryUtils.expandRectangle(bounds, 2);\r
-            minx = bounds.getMinX();\r
-            miny = bounds.getMinY();\r
-            maxx = bounds.getMaxX();\r
-            maxy = bounds.getMaxY();\r
-\r
-            AffineTransform terminalPos = DiagramGraphUtil.getDynamicAffineTransform(graph, terminalElement, terminal);\r
-            //AffineTransform terminalPos2 = DiagramGraphUtil.getAffineTransform(graph, terminal);\r
-            if (terminalPos != null) {\r
-                if (DEBUG) {\r
-                    System.out.println("terminalPos: " + terminalPos);\r
-                    //System.out.println("terminalPos2: " + terminalPos2);\r
-                }\r
-                terminalElementTr.concatenate(terminalPos);\r
-                if (DEBUG)\r
-                    System.out.println("terminalElementTr: " + terminalElementTr);\r
-                x = terminalElementTr.getTranslateX();\r
-                y = terminalElementTr.getTranslateY();\r
-            }\r
-\r
-            Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER);\r
-            if (allowedDirections != null) {\r
-                direction |= allowedDirections;\r
-                direction = RouteGraphUtils.rotateDirection(direction, terminalElementTr);\r
-            } else {\r
-                direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds);\r
-            }\r
-            //System.out.println("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction));\r
-\r
-            backendConnections.add(\r
-                    new BackendConnection(\r
-                            toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin),\r
-                            terminalElement,\r
-                            terminal)\r
-                    );\r
-\r
-            if (direction == 0)\r
-                // Accept any horizontal/vertical direction if nothing is defined\r
-                direction = 0xf;\r
-\r
-            if (graph.<Boolean>getRelatedValue(connector, DIA.Connector_straight, Bindings.BOOLEAN))\r
-                direction |= RouteTerminal.DIR_DIRECT;\r
-            // FIXME: routegraph crashes if this is done for all terminals regardless of the amount of terminals.\r
-\r
-            if (DEBUG)\r
-                System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
-            ILineEndStyle endStyle = loadLineEndStyle(graph, attachmentRelation, connectionType, TAIL);\r
-\r
-            RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle);\r
-            routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) );\r
-\r
-            nodeByData.put( connector, routeTerminal );\r
-\r
-            for (Resource connectedTo : graph.getObjects(connector, DIA.AreConnected)) {\r
-                links.add( new EdgeResource(connectedTo, connector) );\r
-            }\r
-        }\r
-\r
-        // Finish route graph loading by Linking route nodes together\r
-        for (EdgeResource link : links) {\r
-            RouteNode n1 = nodeByData.get(link.first());\r
-            RouteNode n2 = nodeByData.get(link.second());\r
-            if (n1 == null || n2 == null) {\r
-                System.err.println("Stray connection link found: " + link.toString(graph));\r
-                continue;\r
-            }\r
-            rg.link(n1, n2);\r
-        }\r
+        Set<BackendConnection> backendConnections = new THashSet<>();\r
+        RouteGraph rg = RouteGraphUtils.load(graph, diagramRuntime, connection, canvas, diagram, modelingRules, backendConnections);\r
 \r
         // Load connection line style.\r
-        ConnectionStyle style = readConnectionStyle(graph, modelingRules, connection);\r
-        StyledRouteGraphRenderer renderer = getRenderer(graph, style);\r
+        ConnectionStyle style = RouteGraphUtils.readConnectionStyle(graph, modelingRules, connection, STR);\r
+        StyledRouteGraphRenderer renderer = RouteGraphUtils.getRenderer(graph, style);\r
 \r
         // Finish element load\r
         element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg);\r
@@ -389,317 +112,30 @@ public class RouteGraphConnectionClassFactory extends SyncElementFactory {
         element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(diagram, connection, element, backendConnections));\r
 \r
         // Setup graph writeback support for route graph modifications\r
-        final Session session = graph.getSession();\r
+        Session session = graph.getSession();\r
         element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() {\r
             @Override\r
             public void routeGraphChanged(RouteGraphChangeEvent event) {\r
-                scheduleSynchronize(session, connection, event);\r
+                RouteGraphUtils.scheduleSynchronize(session, connection, event);\r
             }\r
         });\r
     }\r
 \r
-    protected EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue)\r
-            throws DatabaseException {\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
-            return EdgeEnd.Begin;\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
-            return EdgeEnd.End;\r
-        return defaultValue;\r
-    }\r
-\r
-    /**\r
-     * @param graph\r
-     * @param canvas\r
-     * @param style\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    protected StyledRouteGraphRenderer getRenderer(ReadGraph graph, ConnectionStyle style)\r
-            throws DatabaseException {\r
-        return graph.syncRequest(new Renderer(style),\r
-                TransientCacheListener.<StyledRouteGraphRenderer>instance());\r
-    }\r
-\r
-    /**\r
-     * A request for caching StyledRouteGraphRenderer results.\r
-     */\r
-    public static class Renderer extends UnaryRead<ConnectionStyle, StyledRouteGraphRenderer> {\r
-        public Renderer(ConnectionStyle style) {\r
-            super(style);\r
-        }\r
-        @Override\r
-        public StyledRouteGraphRenderer perform(ReadGraph graph) throws DatabaseException {\r
-            return new StyledRouteGraphRenderer(parameter);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * @param graph\r
-     * @param canvas\r
-     * @param modelingRules\r
-     * @param connection\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    protected ConnectionStyle readConnectionStyle(ReadGraph graph, IModelingRules modelingRules, Resource connection) throws DatabaseException {\r
-        Resource connectionType = null;\r
-        if (modelingRules != null)\r
-            connectionType = modelingRules.getConnectionType(graph, connection);\r
-        if (connectionType == null)\r
-            connectionType = graph.getPossibleObject(connection, STR.HasConnectionType);\r
-        return readConnectionStyleFromConnectionType(graph, connectionType);\r
-    }\r
-\r
-    protected ConnectionStyle readConnectionStyleFromConnectionType(ReadGraph graph, Resource connectionType) throws DatabaseException {\r
-        return graph.syncRequest(new ReadConnectionStyleFromConnectionType(connectionType),\r
-                TransientCacheListener.<ConnectionStyle>instance());\r
-    }\r
-\r
-    /**\r
-     * A request for caching ConnectionStyle results.\r
-     */\r
-    public static class ReadConnectionStyleFromConnectionType extends UnaryRead<Resource, ConnectionStyle> {\r
-        public ReadConnectionStyleFromConnectionType(Resource connectionType) {\r
-            super(connectionType);\r
-        }\r
-        @Override\r
-        public ConnectionStyle perform(ReadGraph graph) throws DatabaseException {\r
-            return readConnectionStyleFromConnectionType0(graph, parameter);\r
-        }\r
-    }\r
-\r
-    protected static ConnectionStyle readConnectionStyleFromConnectionType0(ReadGraph graph, Resource connectionType) throws DatabaseException {\r
-        ConnectionVisuals cv = null;\r
-        if (connectionType != null)\r
-            cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType),\r
-                    TransientCacheListener.<ConnectionVisuals>instance());\r
-\r
-        // Fixed style settings\r
-        Color branchPointColor = Color.BLACK;\r
-        double branchPointRadius = 0.5;\r
-        double degenerateLineLength = 0.8;\r
-\r
-        Color lineColor = cv != null ? cv.toColor() : null;\r
-        if (lineColor == null)\r
-            lineColor = Color.DARK_GRAY;\r
-        Stroke lineStroke = cv != null ? cv.stroke : null;\r
-        if (lineStroke == null)\r
-            lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0);\r
-        Stroke routeLineStroke = GeometryUtils.scaleStrokeWidth(lineStroke, 2);\r
-\r
-        return new BasicConnectionStyle(\r
-                lineColor,\r
-                branchPointColor,\r
-                branchPointRadius,\r
-                lineStroke,\r
-                routeLineStroke,\r
-                degenerateLineLength);\r
-    }\r
-\r
-    /**\r
-     * @param graph\r
-     * @param connection\r
-     * @param connector\r
-     * @return connection relation statement from diagram connection connector\r
-     *         to a node\r
-     * @throws DatabaseException\r
-     */\r
-    public Statement findTerminalStatement(ReadGraph graph, Resource connection, Resource connector)\r
-            throws DatabaseException {\r
-        for (Statement stm : graph.getStatements(connector, STR.Connects)) {\r
-            if (connection.equals(stm.getObject()))\r
-                continue;\r
-            return stm;\r
-        }\r
-        return null;\r
-    }\r
-\r
-    public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, ILineEndStyle defaultValue)\r
-            throws DatabaseException {\r
-        ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation),\r
-                TransientCacheListener.<ILineEndStyle>instance());\r
-        return style != null ? style : defaultValue;\r
-    }\r
-\r
-    public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, Resource connectionType, ILineEndStyle defaultValue)\r
-            throws DatabaseException {\r
-       if(connectionType != null) {\r
-            ILineEndStyle style = graph.syncRequest(new LineEndStyleWithType(attachmentRelation, connectionType),\r
-                    TransientCacheListener.<ILineEndStyle>instance());\r
-            return style != null ? style : defaultValue;\r
-       } else {\r
-            ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation),\r
-                    TransientCacheListener.<ILineEndStyle>instance());\r
-            return style != null ? style : defaultValue;\r
-       }\r
-    }\r
-\r
-    /**\r
-     * A request for caching ILineEndStyle results.\r
-     */\r
-    public static class LineEndStyle extends UnaryRead<Resource, ILineEndStyle> {\r
-        public LineEndStyle(Resource attachmentRelation) {\r
-            super(attachmentRelation);\r
-        }\r
-        @Override\r
-        public ILineEndStyle perform(ReadGraph graph) throws DatabaseException {\r
-            return loadLineEndStyle0(graph, parameter);\r
-        }\r
-    }\r
-\r
-    public static class LineEndStyleWithType extends ResourceRead2<ILineEndStyle> {\r
-        public LineEndStyleWithType(Resource attachmentRelation, Resource connectionType) {\r
-            super(attachmentRelation, connectionType);\r
-        }\r
-        @Override\r
-        public ILineEndStyle perform(ReadGraph graph) throws DatabaseException {\r
-            return loadLineEndStyle0(graph, resource, resource2);\r
-        }\r
-    }\r
-    \r
-    public static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation)\r
-            throws DatabaseException {\r
-        ILineEndStyle style = graph.getPossibleAdapter(attachmentRelation, ILineEndStyle.class);\r
-        if (style != null)\r
-            return style;\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
-            return HEAD;\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
-            return TAIL;\r
-        return null;\r
-    }\r
-\r
-    public static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation, Resource connectionType)\r
-            throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) {\r
-               if(connectionType != null) {\r
-                       G2DResource G2D = G2DResource.getInstance(graph);\r
-                       Resource end = graph.getPossibleObject(connectionType, G2D.HasEndArrow);\r
-                       if(end != null) {\r
-                               \r
-                               Double size = graph.getPossibleRelatedValue(end, G2D.HasSize, Bindings.DOUBLE);\r
-                               if(size == null) size = 0.0;\r
-                               Double widthRatio = graph.getPossibleRelatedValue(end, G2D.HasWidthRatio, Bindings.DOUBLE);\r
-                               if(widthRatio == null) widthRatio = 1.0;\r
-                               Double space = graph.getPossibleRelatedValue(end, G2D.HasSpace, Bindings.DOUBLE);\r
-                               if(space == null) space = 0.0;\r
-                               \r
-                       Resource c = graph.getPossibleObject(end, G2D.HasColor);\r
-                       Color color = null;\r
-                       if (c != null) {\r
-                           float[] col = graph.getPossibleValue(c, Bindings.FLOAT_ARRAY);\r
-                           if (col != null && col.length >= 3) {\r
-                               color = new Color(col[0], col[1], col[2]);\r
-                           }\r
-                       }\r
-                               \r
-                               return new ArrowLineEndStyle(size, widthRatio*size, space, color);\r
-                               \r
-                       }\r
-               }\r
-        }\r
-        return loadLineEndStyle0(graph, attachmentRelation);\r
-    }\r
-\r
-    /**\r
-     * @param graph\r
-     * @param attachmentRelation\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    public Resource getInverseAttachment(ReadGraph graph, Resource attachmentRelation)\r
-            throws DatabaseException {\r
-        Resource inverse = graph.getPossibleObject(attachmentRelation, DIA.HasInverseAttachment);\r
-        if (inverse != null)\r
-            return inverse;\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
-            return DIA.HasPlainConnector;\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
-            return DIA.HasArrowConnector;\r
-        return null;\r
-    }\r
-\r
-//    private int directionSetToToDirectionMask(DirectionSet directions) {\r
-//        if (directions == DirectionSet.ANY)\r
-//            return 0xf;\r
-//        int mask = 0;\r
-//        for (Double d : directions) {\r
-//            mask |= compassAngleToToDirectionMask(d);\r
-//        }\r
-//        return mask;\r
-//    }\r
-//\r
-//    private int compassAngleToToDirectionMask(double compassAngle) {\r
-//        Double d = DirectionSet.NESW.getClosestDirection(compassAngle);\r
-//        int id = (int) Math.round(d);\r
-//        // bits:\r
-//        // 0 right (1,0)\r
-//        // 1 down  (0,1)\r
-//        // 2 left  (-1,0)\r
-//        // 3 up    (0,-1)\r
-//        switch (id) {\r
-//            case 0: return (1 << 3);\r
-//            case 90: return (1 << 2);\r
-//            case 180: return (1 << 1);\r
-//            case 270: return (1 << 0);\r
-//        }\r
-//        return 0xf;\r
-//    }\r
-\r
-    public Resource resolveFlagAttachment(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException {\r
-        Type type = resolveFlagType(graph, connection, flag, modelingRules);\r
-        if (type != null) {\r
-            switch (type) {\r
-                case In: return DIA.HasPlainConnector;\r
-                case Out: return DIA.HasArrowConnector;\r
-            }\r
-        }\r
-        return null;\r
-    }\r
-\r
-    protected Type resolveFlagType(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException {\r
-        return readFlagType(graph, flag);\r
-//        StructuralResource2 sr = StructuralResource2.getInstance(graph);\r
-//        Resource connectionType = graph.getPossibleObject(connection, sr.HasConnectionType);\r
-//        if (connectionType == null)\r
-//            return readFlagType(graph, flagElement, flag);\r
-//        IFlagTypeReader ftr = graph.getPossibleAdapter(connectionType, IFlagTypeReader.class);\r
-//        if (ftr == null)\r
-//            return readFlagType(graph, flagElement, flag);\r
-//\r
-//        IFlagType ft = ftr.read(graph, flag, modelingRules);\r
-//        FlagInfo info = ft.getInfo(graph);\r
-//        return info.getType();\r
-    }\r
-\r
-    protected Type readFlagType(ReadGraph graph, Resource flag) throws DatabaseException {\r
-        Resource flagType = graph.getPossibleObject(flag, DIA.HasFlagType);\r
-        Type type = DiagramGraphUtil.toFlagType(DIA, flagType);\r
-        return type;\r
-    }\r
-\r
-    public static void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) {\r
-        session.asyncRequest(RouteGraphConnection.synchronizer(connection, event));\r
-    }\r
-\r
     /**\r
      * Must have this in order for {@link TopologicalSelectionExpander} to work.\r
      * Otherwise this is pretty useless and should be deprecated altogether.\r
      * \r
      * @see ElementHints#KEY_CONNECTION_ENTITY\r
      */\r
-    public static class CE implements ConnectionEntity {\r
+    private static class CE implements ConnectionEntity {\r
 \r
         /**\r
-         * Needed to gain access to DataElementMap.\r
+         * Needed to gain access to {@link DataElementMap}.\r
          */\r
         final IDiagram               diagram;\r
-        final DataElementMap         dataMap;\r
 \r
         /**\r
-         * The connection instance resource in the graph backend.\r
+         * The connection instance resource in the graph database back-end.\r
          */\r
         final Resource               connection;\r
 \r
@@ -716,13 +152,12 @@ public class RouteGraphConnectionClassFactory extends SyncElementFactory {
         /**\r
          * Cache.\r
          */\r
-        Set<Connection>              terminalConnections;\r
+        transient Set<Connection>    terminalConnections;\r
 \r
         public CE(IDiagram diagram, Resource connection, IElement connectionElement, Set<BackendConnection> backendConnections) {\r
             if (connectionElement == null)\r
                 throw new NullPointerException("null connection element");\r
             this.diagram = diagram;\r
-            this.dataMap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
             this.connection = connection;\r
             this.connectionElement = connectionElement;\r
             this.backendConnections = backendConnections;\r
@@ -806,42 +241,4 @@ public class RouteGraphConnectionClassFactory extends SyncElementFactory {
 \r
     }\r
 \r
-    public static class BackendConnection {\r
-        public final Resource node;\r
-        public final Resource terminal;\r
-        public final EdgeEnd  end;\r
-        public BackendConnection(EdgeEnd end, Resource node, Resource terminal) {\r
-            assert end != null;\r
-            assert node != null;\r
-            assert terminal != null;\r
-            this.end = end;\r
-            this.node = node;\r
-            this.terminal = terminal;\r
-        }\r
-        @Override\r
-        public boolean equals(Object obj) {\r
-            if (this == obj)\r
-                return true;\r
-            if (!(obj instanceof Connection))\r
-                return false;\r
-            Connection other = (Connection) obj;\r
-            return other.terminal == terminal\r
-                    && other.node == node\r
-                    && other.end == end;\r
-        }\r
-        @Override\r
-        public int hashCode() {\r
-            final int prime = 31;\r
-            int result = 1;\r
-            result = prime * result + end.hashCode();\r
-            result = prime * result + ((node == null) ? 0 : node.hashCode());\r
-            result = prime * result + ((terminal == null) ? 0 : terminal.hashCode());\r
-            return result;\r
-        }\r
-        @Override\r
-        public String toString() {\r
-            return "BackendConnection[node=" + node + ", terminal=" + terminal + ", end=" + end + "]";\r
-        }\r
-    }\r
-\r
 }
\ No newline at end of file
index bba694a5e90a4d7beaaa45bb7e968b59614bb48e..a957377f81dea0c8deb6ec8e3df104af27f9de47 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************\r
- * Copyright (c) 2012 Association for Decentralized Information Management\r
+ * Copyright (c) 2012, 2016 Association for Decentralized Information Management\r
  * in 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
@@ -8,13 +8,14 @@
  *\r
  * Contributors:\r
  *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *     Semantum Oy - refactoring\r
  *******************************************************************************/\r
 package org.simantics.diagram.adapter;\r
 \r
-import gnu.trove.map.hash.THashMap;\r
-import gnu.trove.set.hash.THashSet;\r
-\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
 import java.awt.Shape;\r
+import java.awt.Stroke;\r
 import java.awt.geom.AffineTransform;\r
 import java.awt.geom.Rectangle2D;\r
 import java.util.Collection;\r
@@ -25,17 +26,22 @@ import java.util.Set;
 import org.simantics.databoard.Bindings;\r
 import org.simantics.db.ReadGraph;\r
 import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
 import org.simantics.db.Statement;\r
 import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
+import org.simantics.db.common.request.ResourceRead2;\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.diagram.adapter.RouteGraphConnectionClassFactory.BackendConnection;\r
+import org.simantics.diagram.connection.ConnectionVisuals;\r
 import org.simantics.diagram.connection.RouteGraph;\r
 import org.simantics.diagram.connection.RouteGraphConnectionClass;\r
 import org.simantics.diagram.connection.RouteLine;\r
 import org.simantics.diagram.connection.RouteNode;\r
 import org.simantics.diagram.connection.RouteTerminal;\r
+import org.simantics.diagram.connection.rendering.BasicConnectionStyle;\r
+import org.simantics.diagram.connection.rendering.ConnectionStyle;\r
+import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;\r
 import org.simantics.diagram.connection.rendering.arrows.ArrowLineEndStyle;\r
 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;\r
 import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle;\r
@@ -43,17 +49,20 @@ import org.simantics.diagram.content.EdgeResource;
 import org.simantics.diagram.content.TerminalMap;\r
 import org.simantics.diagram.query.DiagramRequests;\r
 import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.stubs.G2DResource;\r
 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
 import org.simantics.diagram.synchronization.graph.RouteGraphConnection;\r
 import org.simantics.g2d.canvas.ICanvasContext;\r
 import org.simantics.g2d.canvas.impl.CanvasContext;\r
 import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.handler.Topology.Connection;\r
 import org.simantics.g2d.diagram.impl.ElementDiagram;\r
 import org.simantics.g2d.element.ElementUtils;\r
 import org.simantics.g2d.element.IElement;\r
 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
 import org.simantics.g2d.elementclass.FlagClass.Type;\r
 import org.simantics.layer0.Layer0;\r
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;\r
 import org.simantics.scenegraph.utils.GeometryUtils;\r
 import org.simantics.structural.stubs.StructuralResource2;\r
 import org.simantics.structural2.modelingRules.CPTerminal;\r
@@ -61,126 +70,33 @@ import org.simantics.structural2.modelingRules.IAttachmentRelationMap;
 import org.simantics.structural2.modelingRules.IModelingRules;\r
 import org.simantics.utils.threads.CurrentThread;\r
 \r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
 public class RouteGraphUtils {\r
-       \r
-       public static boolean DEBUG = false;\r
-       \r
+\r
+    public static boolean DEBUG = false;\r
+\r
     public static final ILineEndStyle HEAD  = new ArrowLineEndStyle("fill 2 1 0");\r
     public static final ILineEndStyle TAIL  = PlainLineEndStyle.INSTANCE;\r
 \r
-    private static EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue)\r
-            throws DatabaseException {\r
-               DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
-            return EdgeEnd.Begin;\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
-            return EdgeEnd.End;\r
-        return defaultValue;\r
-    }\r
-    \r
-    private static Resource resolveFlagAttachment(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException {\r
-               DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        Type type = resolveFlagType(graph, connection, flag, modelingRules);\r
-        if (type != null) {\r
-            switch (type) {\r
-                case In: return DIA.HasPlainConnector;\r
-                case Out: return DIA.HasArrowConnector;\r
-            }\r
-        }\r
-        return null;\r
-    }\r
-\r
-    private static Type resolveFlagType(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException {\r
-        return readFlagType(graph, flag);\r
+    public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection) throws DatabaseException {\r
+        ICanvasContext canvas = new CanvasContext(CurrentThread.getThreadAccess());\r
+        IDiagram diagram = new ElementDiagram(canvas);\r
+        return load(graph, diagramRuntime, connection, canvas, diagram);\r
     }\r
 \r
-    private static Type readFlagType(ReadGraph graph, Resource flag) throws DatabaseException {\r
-               DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        Resource flagType = graph.getPossibleObject(flag, DIA.HasFlagType);\r
-        Type type = DiagramGraphUtil.toFlagType(DIA, flagType);\r
-        return type;\r
+    public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram) throws DatabaseException {\r
+        Layer0 L0 = Layer0.getInstance(graph);\r
+        Resource diagramResource = graph.getPossibleObject(connection, L0.PartOf);\r
+        IModelingRules modelingRules = graph.syncRequest(DiagramRequests.getModelingRules(diagramResource, null), TransientCacheListener.<IModelingRules>instance());\r
+        return load(graph, diagramRuntime, connection, canvas, diagram, modelingRules, null);\r
     }\r
 \r
-//    public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, ILineEndStyle defaultValue)\r
-//            throws DatabaseException {\r
-//        ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation),\r
-//                TransientCacheListener.<ILineEndStyle>instance());\r
-//        return style != null ? style : defaultValue;\r
-//    }\r
+    public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram, IModelingRules modelingRules, Set<BackendConnection> backendConnections) throws DatabaseException {\r
 \r
-    /**\r
-     * A request for caching ILineEndStyle results.\r
-     */\r
-    public static class LineEndStyle extends UnaryRead<Resource, ILineEndStyle> {\r
-        public LineEndStyle(Resource attachmentRelation) {\r
-            super(attachmentRelation);\r
-        }\r
-        @Override\r
-        public ILineEndStyle perform(ReadGraph graph) throws DatabaseException {\r
-            return loadLineEndStyle0(graph, parameter);\r
-        }\r
-    }\r
-\r
-    public static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation)\r
-            throws DatabaseException {\r
-        ILineEndStyle style = graph.getPossibleAdapter(attachmentRelation, ILineEndStyle.class);\r
-        if (style != null)\r
-            return style;\r
         DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
-            return HEAD;\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
-            return TAIL;\r
-        return null;\r
-    }\r
-    \r
-    private static  Statement findTerminalStatement(ReadGraph graph, Resource connection, Resource connector)\r
-            throws DatabaseException {\r
-       \r
-       StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
-       \r
-        for (Statement stm : graph.getStatements(connector, STR.Connects)) {\r
-            if (connection.equals(stm.getObject()))\r
-                continue;\r
-            return stm;\r
-        }\r
-        return null;\r
-        \r
-    }\r
-\r
-    private static  Resource getInverseAttachment(ReadGraph graph, Resource attachmentRelation)\r
-            throws DatabaseException {\r
-               DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        Resource inverse = graph.getPossibleObject(attachmentRelation, DIA.HasInverseAttachment);\r
-        if (inverse != null)\r
-            return inverse;\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
-            return DIA.HasPlainConnector;\r
-        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
-            return DIA.HasArrowConnector;\r
-        return null;\r
-    }\r
-\r
-       public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection) throws DatabaseException {\r
-\r
-               ICanvasContext canvas = new CanvasContext(CurrentThread.getThreadAccess());\r
-               IDiagram diagram = new ElementDiagram(canvas);\r
-               return load(graph, diagramRuntime, connection, canvas, diagram);\r
-\r
-       }\r
-\r
-    public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram) throws DatabaseException {\r
-               \r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-               DiagramResource DIA = DiagramResource.getInstance(graph);\r
-               StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
-               \r
-               Resource diagramResource = graph.getPossibleObject(connection, L0.PartOf);\r
-               \r
-        IModelingRules modelingRules = graph.syncRequest(DiagramRequests.getModelingRules(diagramResource, null), TransientCacheListener.<IModelingRules>instance());\r
-\r
-//        IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);\r
-//        Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);\r
+        StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
 \r
         RouteGraph rg = new RouteGraph();\r
 \r
@@ -232,7 +148,7 @@ public class RouteGraphUtils {
             for (Statement toConnector : toConnectorStatements) {\r
                 Resource connector = toConnector.getObject();\r
 \r
-                Statement terminalStm = findTerminalStatement(graph, connection, connector);\r
+                Statement terminalStm = findTerminalStatement(graph, connection, connector, STR);\r
                 if (terminalStm == null)\r
                     // Ignore broken connector: attached to the connection but not to any terminal.\r
                     continue;\r
@@ -255,7 +171,7 @@ public class RouteGraphUtils {
                         System.out.println("modeling rules decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");\r
                 } else if (graph.isInstanceOf(terminalElement, DIA.Flag)) {\r
                     // Secondary: believe flag type\r
-                    attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules);\r
+                    attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules, DIA);\r
                     if (attachment != null) {\r
                         if (connectorToModeledAttachment == null)\r
                             connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());\r
@@ -272,16 +188,13 @@ public class RouteGraphUtils {
 \r
         Resource forcedAttachmentRelation = null;\r
         if (terminalCount == 2 && connectorToModeledAttachment.size() == 1) {\r
-            forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next());\r
+            forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next(), DIA);\r
             if (DEBUG)\r
                 System.out.println("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation));\r
         }\r
 \r
         Resource connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); \r
 \r
-        // Needed to support ConnectionEntity#getTerminalConnections\r
-        Set<BackendConnection> backendConnections = new THashSet<BackendConnection>(toConnectorStatements.size(), 0.75f);\r
-\r
         // Load all node terminal connections as RouteTerminals\r
         for (Statement toConnector : toConnectorStatements) {\r
             Resource connector = toConnector.getObject();\r
@@ -289,7 +202,7 @@ public class RouteGraphUtils {
             if (DEBUG)\r
                 System.out.println("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
 \r
-            Statement terminalStm = findTerminalStatement(graph, connection, connector);\r
+            Statement terminalStm = findTerminalStatement(graph, connection, connector, STR);\r
             if (terminalStm == null)\r
                 // Ignore broken connector: attached to the connection but not to any terminal.\r
                 continue;\r
@@ -326,9 +239,9 @@ public class RouteGraphUtils {
                 System.out.println("terminal: " + graph.getURI(terminalStm.getPredicate()));\r
             }\r
             AffineTransform terminalElementTr = diagramRuntime != null ?\r
-                       DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement) : \r
-                       DiagramGraphUtil.getWorldTransform(graph, terminalElement);\r
-            \r
+                    DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement) : \r
+                    DiagramGraphUtil.getWorldTransform(graph, terminalElement);\r
+\r
             if (DEBUG)\r
                 System.out.println("terminalElementTr: " + terminalElementTr);\r
 \r
@@ -352,7 +265,7 @@ public class RouteGraphUtils {
             if (DEBUG)\r
                 System.out.println("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
 \r
-//            // Get element bounds to decide allowed terminal direction(s)\r
+            // Get element bounds to decide allowed terminal direction(s)\r
             IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));\r
             ElementUtils.getElementBounds(te, bounds);\r
             {\r
@@ -392,12 +305,14 @@ public class RouteGraphUtils {
             }\r
             //System.out.println("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction));\r
 \r
-            backendConnections.add(\r
-                    new BackendConnection(\r
-                            toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin),\r
-                            terminalElement,\r
-                            terminal)\r
-                    );\r
+            if (backendConnections != null) {\r
+                backendConnections.add(\r
+                        new BackendConnection(\r
+                                toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin, DIA),\r
+                                terminalElement,\r
+                                terminal)\r
+                        );\r
+            }\r
 \r
             if (direction == 0)\r
                 // Accept any horizontal/vertical direction if nothing is defined\r
@@ -409,7 +324,7 @@ public class RouteGraphUtils {
 \r
             if (DEBUG)\r
                 System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
-            ILineEndStyle endStyle = RouteGraphConnectionClassFactory.loadLineEndStyle(graph, attachmentRelation, connectionType, RouteGraphConnectionClassFactory.TAIL);\r
+            ILineEndStyle endStyle = loadLineEndStyle(graph, attachmentRelation, connectionType, TAIL);\r
 \r
             RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle);\r
             routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) );\r
@@ -432,30 +347,241 @@ public class RouteGraphUtils {
             rg.link(n1, n2);\r
         }\r
 \r
-//        // Load connection line style.\r
-//        ConnectionStyle style = readConnectionStyle(graph, canvas, modelingRules, connection);\r
-//        StyledRouteGraphRenderer renderer = getRenderer(graph, canvas, style);\r
-//\r
-//        // Finish element load\r
-//        element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg);\r
-//        element.setHint(RouteGraphConnectionClass.KEY_RENDERER, renderer);\r
-//        element.setHint(RouteGraphConnectionClass.KEY_PICK_TOLERANCE, 0.5);\r
-//\r
-//        // Initialize ConnectionEntity in element\r
-//        element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(diagram, connection, element, backendConnections));\r
-//\r
-//        // Setup graph writeback support for route graph modifications\r
-//        final Session session = graph.getSession();\r
-//        element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() {\r
-//            @Override\r
-//            public void routeGraphChanged(RouteGraphChangeEvent event) {\r
-//                scheduleSynchronize(session, connection, event);\r
-//            }\r
-//        });\r
-        \r
         return rg;\r
 \r
-       }\r
+    }\r
+\r
+    private static EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue, DiagramResource DIA)\r
+            throws DatabaseException {\r
+        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
+            return EdgeEnd.Begin;\r
+        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
+            return EdgeEnd.End;\r
+        return defaultValue;\r
+    }\r
+\r
+    private static Resource resolveFlagAttachment(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules, DiagramResource DIA) throws DatabaseException {\r
+        Type type = resolveFlagType(graph, connection, flag, modelingRules, DIA);\r
+        if (type != null) {\r
+            switch (type) {\r
+                case In: return DIA.HasPlainConnector;\r
+                case Out: return DIA.HasArrowConnector;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static Type resolveFlagType(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules, DiagramResource DIA) throws DatabaseException {\r
+        return readFlagType(graph, flag, DIA);\r
+    }\r
+\r
+    private static Type readFlagType(ReadGraph graph, Resource flag, DiagramResource DIA) throws DatabaseException {\r
+        Resource flagType = graph.getPossibleObject(flag, DIA.HasFlagType);\r
+        Type type = DiagramGraphUtil.toFlagType(DIA, flagType);\r
+        return type;\r
+    }\r
+\r
+    private static Statement findTerminalStatement(ReadGraph graph, Resource connection, Resource connector, StructuralResource2 STR)\r
+            throws DatabaseException {\r
+        for (Statement stm : graph.getStatements(connector, STR.Connects)) {\r
+            if (connection.equals(stm.getObject()))\r
+                continue;\r
+            return stm;\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static Resource getInverseAttachment(ReadGraph graph, Resource attachmentRelation, DiagramResource DIA)\r
+            throws DatabaseException {\r
+        Resource inverse = graph.getPossibleObject(attachmentRelation, DIA.HasInverseAttachment);\r
+        if (inverse != null)\r
+            return inverse;\r
+        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
+            return DIA.HasPlainConnector;\r
+        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
+            return DIA.HasArrowConnector;\r
+        return null;\r
+    }\r
+\r
+    public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, ILineEndStyle defaultValue)\r
+            throws DatabaseException {\r
+        ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation),\r
+                TransientCacheListener.<ILineEndStyle>instance());\r
+        return style != null ? style : defaultValue;\r
+    }\r
+\r
+    public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, Resource connectionType, ILineEndStyle defaultValue)\r
+            throws DatabaseException {\r
+       if(connectionType != null) {\r
+            ILineEndStyle style = graph.syncRequest(new LineEndStyleWithType(attachmentRelation, connectionType),\r
+                    TransientCacheListener.<ILineEndStyle>instance());\r
+            return style != null ? style : defaultValue;\r
+       } else {\r
+            ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation),\r
+                    TransientCacheListener.<ILineEndStyle>instance());\r
+            return style != null ? style : defaultValue;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * A request for caching ILineEndStyle results.\r
+     */\r
+    public static class LineEndStyle extends UnaryRead<Resource, ILineEndStyle> {\r
+        public LineEndStyle(Resource attachmentRelation) {\r
+            super(attachmentRelation);\r
+        }\r
+        @Override\r
+        public ILineEndStyle perform(ReadGraph graph) throws DatabaseException {\r
+            return loadLineEndStyle0(graph, parameter);\r
+        }\r
+    }\r
+\r
+    public static class LineEndStyleWithType extends ResourceRead2<ILineEndStyle> {\r
+        public LineEndStyleWithType(Resource attachmentRelation, Resource connectionType) {\r
+            super(attachmentRelation, connectionType);\r
+        }\r
+        @Override\r
+        public ILineEndStyle perform(ReadGraph graph) throws DatabaseException {\r
+            return loadLineEndStyle0(graph, resource, resource2);\r
+        }\r
+    }\r
+    \r
+    private static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation)\r
+            throws DatabaseException {\r
+        ILineEndStyle style = graph.getPossibleAdapter(attachmentRelation, ILineEndStyle.class);\r
+        if (style != null)\r
+            return style;\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))\r
+            return HEAD;\r
+        if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))\r
+            return TAIL;\r
+        return null;\r
+    }\r
+\r
+    private static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation, Resource connectionType)\r
+            throws DatabaseException {\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) {\r
+            if(connectionType != null) {\r
+                G2DResource G2D = G2DResource.getInstance(graph);\r
+                Resource end = graph.getPossibleObject(connectionType, G2D.HasEndArrow);\r
+                if(end != null) {\r
+                    Double size = graph.getPossibleRelatedValue(end, G2D.HasSize, Bindings.DOUBLE);\r
+                    if(size == null) size = 0.0;\r
+                    Double widthRatio = graph.getPossibleRelatedValue(end, G2D.HasWidthRatio, Bindings.DOUBLE);\r
+                    if(widthRatio == null) widthRatio = 1.0;\r
+                    Double space = graph.getPossibleRelatedValue(end, G2D.HasSpace, Bindings.DOUBLE);\r
+                    if(space == null) space = 0.0;\r
+\r
+                    Resource c = graph.getPossibleObject(end, G2D.HasColor);\r
+                    Color color = null;\r
+                    if (c != null) {\r
+                        float[] col = graph.getPossibleValue(c, Bindings.FLOAT_ARRAY);\r
+                        if (col != null && col.length >= 3) {\r
+                            color = new Color(col[0], col[1], col[2]);\r
+                        }\r
+                    }\r
+\r
+                    return new ArrowLineEndStyle(size, widthRatio*size, space, color);\r
+                }\r
+            }\r
+        }\r
+        return loadLineEndStyle0(graph, attachmentRelation);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param canvas\r
+     * @param style\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    protected static StyledRouteGraphRenderer getRenderer(ReadGraph graph, ConnectionStyle style)\r
+            throws DatabaseException {\r
+        return graph.syncRequest(new Renderer(style),\r
+                TransientCacheListener.<StyledRouteGraphRenderer>instance());\r
+    }\r
+\r
+    /**\r
+     * A request for caching StyledRouteGraphRenderer results.\r
+     */\r
+    public static class Renderer extends UnaryRead<ConnectionStyle, StyledRouteGraphRenderer> {\r
+        public Renderer(ConnectionStyle style) {\r
+            super(style);\r
+        }\r
+        @Override\r
+        public StyledRouteGraphRenderer perform(ReadGraph graph) throws DatabaseException {\r
+            return new StyledRouteGraphRenderer(parameter);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param canvas\r
+     * @param modelingRules\r
+     * @param connection\r
+     * @return\r
+     * @throws DatabaseException\r
+     */\r
+    protected static ConnectionStyle readConnectionStyle(ReadGraph graph, IModelingRules modelingRules, Resource connection, StructuralResource2 STR) throws DatabaseException {\r
+        Resource connectionType = null;\r
+        if (modelingRules != null)\r
+            connectionType = modelingRules.getConnectionType(graph, connection);\r
+        if (connectionType == null)\r
+            connectionType = graph.getPossibleObject(connection, STR.HasConnectionType);\r
+        return readConnectionStyleFromConnectionType(graph, connectionType);\r
+    }\r
+\r
+    protected static ConnectionStyle readConnectionStyleFromConnectionType(ReadGraph graph, Resource connectionType) throws DatabaseException {\r
+        return graph.syncRequest(new ReadConnectionStyleFromConnectionType(connectionType),\r
+                TransientCacheListener.<ConnectionStyle>instance());\r
+    }\r
+\r
+    /**\r
+     * A request for caching ConnectionStyle results.\r
+     */\r
+    public static class ReadConnectionStyleFromConnectionType extends UnaryRead<Resource, ConnectionStyle> {\r
+        public ReadConnectionStyleFromConnectionType(Resource connectionType) {\r
+            super(connectionType);\r
+        }\r
+        @Override\r
+        public ConnectionStyle perform(ReadGraph graph) throws DatabaseException {\r
+            return readConnectionStyleFromConnectionType0(graph, parameter);\r
+        }\r
+    }\r
+\r
+    protected static ConnectionStyle readConnectionStyleFromConnectionType0(ReadGraph graph, Resource connectionType) throws DatabaseException {\r
+        ConnectionVisuals cv = null;\r
+        if (connectionType != null)\r
+            cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType),\r
+                    TransientCacheListener.<ConnectionVisuals>instance());\r
+\r
+        // Fixed style settings\r
+        Color branchPointColor = Color.BLACK;\r
+        double branchPointRadius = 0.5;\r
+        double degenerateLineLength = 0.8;\r
+\r
+        Color lineColor = cv != null ? cv.toColor() : null;\r
+        if (lineColor == null)\r
+            lineColor = Color.DARK_GRAY;\r
+        Stroke lineStroke = cv != null ? cv.stroke : null;\r
+        if (lineStroke == null)\r
+            lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0);\r
+        Stroke routeLineStroke = GeometryUtils.scaleStrokeWidth(lineStroke, 2);\r
+\r
+        return new BasicConnectionStyle(\r
+                lineColor,\r
+                branchPointColor,\r
+                branchPointRadius,\r
+                lineStroke,\r
+                routeLineStroke,\r
+                degenerateLineLength);\r
+    }\r
+\r
+    public static void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) {\r
+        session.asyncRequest(RouteGraphConnection.synchronizer(connection, event));\r
+    }\r
 \r
     // ------------------------------------------------------------------------\r
     // RouteGraph RouteTerminal allowed direction rotation support\r
@@ -531,4 +657,47 @@ public class RouteGraphUtils {
     private static final double DEG_45 = Math.PI/4.;\r
     private static final double DEG_135 = Math.PI*3./4.;\r
 \r
+    static class BackendConnection {\r
+        public final Resource node;\r
+        public final Resource terminal;\r
+        public final EdgeEnd  end;\r
+        public final int hash;\r
+        public BackendConnection(EdgeEnd end, Resource node, Resource terminal) {\r
+            assert end != null;\r
+            assert node != null;\r
+            assert terminal != null;\r
+            this.end = end;\r
+            this.node = node;\r
+            this.terminal = terminal;\r
+            this.hash = makeHash();\r
+        }\r
+        @Override\r
+        public boolean equals(Object obj) {\r
+            if (this == obj)\r
+                return true;\r
+            if (!(obj instanceof Connection))\r
+                return false;\r
+            Connection other = (Connection) obj;\r
+            return other.terminal == terminal\r
+                    && other.node == node\r
+                    && other.end == end;\r
+        }\r
+        private int makeHash() {\r
+            final int prime = 31;\r
+            int result = 1;\r
+            result = prime * result + end.hashCode();\r
+            result = prime * result + ((node == null) ? 0 : node.hashCode());\r
+            result = prime * result + ((terminal == null) ? 0 : terminal.hashCode());\r
+            return result;\r
+        }\r
+        @Override\r
+        public int hashCode() {\r
+            return hash;\r
+        }\r
+        @Override\r
+        public String toString() {\r
+            return "BackendConnection[node=" + node + ", terminal=" + terminal + ", end=" + end + "]";\r
+        }\r
+    }\r
+\r
 }\r