-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 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
- * 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.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.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
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.handler.DataElementMap;\r
-import org.simantics.g2d.diagram.handler.Topology.Connection;\r
-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
-/**\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
- * \r
- * @author Tuukka Lehtonen\r
- */\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
-\r
- protected Layer0 L0;\r
- protected DiagramResource DIA;\r
- protected StructuralResource2 STR;\r
- protected ModelingResources MOD;\r
-\r
- public RouteGraphConnectionClassFactory(ReadGraph graph) {\r
- this.L0 = Layer0.getInstance(graph);\r
- this.DIA = DiagramResource.getInstance(graph);\r
- this.STR = StructuralResource2.getInstance(graph);\r
- this.MOD = ModelingResources.getInstance(graph);\r
- }\r
-\r
- @Override\r
- public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType,\r
- final AsyncProcedure<ElementClass> procedure) {\r
- procedure.execute(graph, CLASS.newClassWith(false, new StaticObjectAdapter(elementType)));\r
- }\r
-\r
- @Override\r
- protected Resource getElementClassBaseType(AsyncReadGraph graph) {\r
- return DIA.Connection;\r
- }\r
-\r
- @Override\r
- public void load(ReadGraph graph, ICanvasContext canvas, IDiagram diagram, final Resource connection,\r
- IElement element) throws DatabaseException {\r
-\r
- 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
-\r
- // Load connection line style.\r
- ConnectionStyle style = readConnectionStyle(graph, modelingRules, connection);\r
- StyledRouteGraphRenderer renderer = getRenderer(graph, 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
-\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
-\r
- /**\r
- * Needed to gain access to DataElementMap.\r
- */\r
- final IDiagram diagram;\r
- final DataElementMap dataMap;\r
-\r
- /**\r
- * The connection instance resource in the graph backend.\r
- */\r
- final Resource connection;\r
-\r
- /**\r
- * The current element mapped to connection. \r
- */\r
- IElement connectionElement;\r
-\r
- /**\r
- * @see #getTerminalConnections(Collection)\r
- */\r
- final Set<BackendConnection> backendConnections;\r
-\r
- /**\r
- * Cache.\r
- */\r
- 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
- IElement ce = getConnection0();\r
- if (ce != null)\r
- this.connectionElement = ce;\r
- }\r
-\r
- public IElement getConnection0() {\r
- DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
- IElement connectionElement = dem.getElement(diagram, connection);\r
- return connectionElement;\r
- }\r
-\r
- @Override\r
- public IElement getConnection() {\r
- IElement c = getConnection0();\r
- if (c == null)\r
- c = this.connectionElement;\r
- return c;\r
- }\r
-\r
- @Override\r
- public Collection<IElement> getBranchPoints(Collection<IElement> result) {\r
- return result != null ? result : Collections.<IElement> emptyList();\r
- }\r
-\r
- @Override\r
- public Collection<IElement> getSegments(Collection<IElement> result) {\r
- return result != null ? result : Collections.<IElement> emptyList();\r
- }\r
-\r
- @Override\r
- public Collection<Connection> getTerminalConnections(Collection<Connection> result) {\r
- if (terminalConnections == null)\r
- terminalConnections = calculateTerminalConnections();\r
- if (result == null)\r
- result = new ArrayList<Connection>(terminalConnections);\r
- else\r
- result.addAll(terminalConnections);\r
- return terminalConnections;\r
- }\r
-\r
- private Set<Connection> calculateTerminalConnections() {\r
- Set<Connection> result = new THashSet<Connection>(backendConnections.size());\r
- DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
- IElement connectionElement = dem.getElement(diagram, connection);\r
- if (connectionElement == null)\r
- throw new NullPointerException("connection is not mapped");\r
- ArrayList<Terminal> ts = new ArrayList<Terminal>();\r
- for (BackendConnection bc : backendConnections) {\r
- IElement e = dem.getElement(diagram, bc.node);\r
- if (e == null)\r
- continue;\r
- TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class);\r
- ts.clear();\r
- tt.getTerminals(e, ts);\r
- for (Terminal t : ts) {\r
- if (t instanceof ResourceTerminal) {\r
- ResourceTerminal rt = (ResourceTerminal) t;\r
- if (bc.terminal.equals(rt.getResource())) {\r
- result.add(new Connection(connectionElement, bc.end, e, t));\r
- break;\r
- }\r
- }\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- @Override\r
- public void setListener(ConnectionListener listener) {\r
- throw new UnsupportedOperationException();\r
- }\r
-\r
- @Override\r
- public String toString() {\r
- return getClass().getSimpleName() + "[resource=" + connection + ", connectionElement=" + getConnection()\r
- + "]";\r
- }\r
-\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
+/*******************************************************************************
+ * Copyright (c) 2007, 2016 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
+ * Semantum Oy - refactoring
+ *******************************************************************************/
+package org.simantics.diagram.adapter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import org.simantics.db.AsyncReadGraph;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.procedure.AsyncProcedure;
+import org.simantics.diagram.adapter.RouteGraphUtils.BackendConnection;
+import org.simantics.diagram.connection.RouteGraph;
+import org.simantics.diagram.connection.RouteGraphConnectionClass;
+import org.simantics.diagram.connection.rendering.ConnectionStyle;
+import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;
+import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
+import org.simantics.diagram.content.ResourceTerminal;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.diagram.ui.DiagramModelHints;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.connection.ConnectionEntity;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.DataElementMap;
+import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.diagram.handler.Topology.Terminal;
+import org.simantics.g2d.element.ElementClass;
+import org.simantics.g2d.element.ElementHints;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.TerminalTopology;
+import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;
+import org.simantics.g2d.utils.TopologicalSelectionExpander;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.simantics.structural2.modelingRules.IModelingRules;
+
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * An element class for single connection entity elements. A connection entity
+ * consists of connection edge segments and branch points as its children.
+ *
+ * @author Tuukka Lehtonen
+ */
+public class RouteGraphConnectionClassFactory extends SyncElementFactory {
+
+ public static final ElementClass CLASS = RouteGraphConnectionClass.CLASS;
+
+ public static final ILineEndStyle HEAD = RouteGraphUtils.HEAD;
+ public static final ILineEndStyle TAIL = RouteGraphUtils.TAIL;
+
+ protected Layer0 L0;
+ protected DiagramResource DIA;
+ protected StructuralResource2 STR;
+ protected ModelingResources MOD;
+
+ public RouteGraphConnectionClassFactory(ReadGraph graph) {
+ this.L0 = Layer0.getInstance(graph);
+ this.DIA = DiagramResource.getInstance(graph);
+ this.STR = StructuralResource2.getInstance(graph);
+ this.MOD = ModelingResources.getInstance(graph);
+ }
+
+ @Override
+ public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType,
+ final AsyncProcedure<ElementClass> procedure) {
+ procedure.execute(graph, CLASS.newClassWith(false, new StaticObjectAdapter(elementType)));
+ }
+
+ @Override
+ protected Resource getElementClassBaseType(AsyncReadGraph graph) {
+ return DIA.Connection;
+ }
+
+ @Override
+ public void load(ReadGraph graph, ICanvasContext canvas, IDiagram diagram, final Resource connection,
+ IElement element) throws DatabaseException {
+
+ IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);
+ Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);
+
+ Set<BackendConnection> backendConnections = new THashSet<>();
+ RouteGraph rg = RouteGraphUtils.load(graph, diagramRuntime, connection, canvas, diagram, element, modelingRules, backendConnections);
+
+ // Load connection line style.
+ ConnectionStyle style = RouteGraphUtils.readConnectionStyle(graph, modelingRules, connection, STR);
+ StyledRouteGraphRenderer renderer = RouteGraphUtils.getRenderer(graph, style);
+
+ // Finish element load
+ element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg);
+ element.setHint(RouteGraphConnectionClass.KEY_RENDERER, renderer);
+ element.setHint(RouteGraphConnectionClass.KEY_PICK_TOLERANCE, 0.5);
+
+ // Initialize ConnectionEntity in element
+ element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(diagram, connection, element, backendConnections));
+
+ // Setup graph writeback support for route graph modifications
+ Session session = graph.getSession();
+ element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() {
+ @Override
+ public void routeGraphChanged(RouteGraphChangeEvent event) {
+ RouteGraphUtils.scheduleSynchronize(session, connection, event);
+ }
+ });
+ }
+
+ /**
+ * Must have this in order for {@link TopologicalSelectionExpander} to work.
+ * Otherwise this is pretty useless and should be deprecated altogether.
+ *
+ * @see ElementHints#KEY_CONNECTION_ENTITY
+ */
+ public static class CE implements ConnectionEntity {
+
+ /**
+ * Needed to gain access to {@link DataElementMap}.
+ */
+ final IDiagram diagram;
+
+ /**
+ * The connection instance resource in the graph database back-end.
+ */
+ final Resource connection;
+
+ /**
+ * The current element mapped to connection.
+ */
+ IElement connectionElement;
+
+ /**
+ * @see #getTerminalConnections(Collection)
+ */
+ final Set<BackendConnection> backendConnections;
+
+ /**
+ * Cache.
+ */
+ transient Set<Connection> terminalConnections;
+
+ public CE(IDiagram diagram, Resource connection, IElement connectionElement, Set<BackendConnection> backendConnections) {
+ if (connectionElement == null)
+ throw new NullPointerException("null connection element");
+ this.diagram = diagram;
+ this.connection = connection;
+ this.connectionElement = connectionElement;
+ this.backendConnections = backendConnections;
+ IElement ce = getConnection0();
+ if (ce != null)
+ this.connectionElement = ce;
+ }
+
+ public IElement getConnection0() {
+ DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
+ IElement connectionElement = dem.getElement(diagram, connection);
+ return connectionElement;
+ }
+
+ @Override
+ public IElement getConnection() {
+ IElement c = getConnection0();
+ if (c == null)
+ c = this.connectionElement;
+ return c;
+ }
+
+ @Override
+ public Collection<IElement> getBranchPoints(Collection<IElement> result) {
+ return result != null ? result : Collections.<IElement> emptyList();
+ }
+
+ @Override
+ public Collection<IElement> getSegments(Collection<IElement> result) {
+ return result != null ? result : Collections.<IElement> emptyList();
+ }
+
+ @Override
+ public Collection<Connection> getTerminalConnections(Collection<Connection> result) {
+ if (terminalConnections == null)
+ terminalConnections = calculateTerminalConnections();
+ if (result == null)
+ result = new ArrayList<Connection>(terminalConnections);
+ else
+ result.addAll(terminalConnections);
+ return terminalConnections;
+ }
+
+ private Set<Connection> calculateTerminalConnections() {
+ Set<Connection> result = new THashSet<Connection>(backendConnections.size());
+ DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
+ IElement connectionElement = dem.getElement(diagram, connection);
+ if (connectionElement == null)
+ throw new NullPointerException("connection is not mapped");
+ ArrayList<Terminal> ts = new ArrayList<Terminal>();
+ for (BackendConnection bc : backendConnections) {
+ IElement e = dem.getElement(diagram, bc.node);
+ if (e == null)
+ continue;
+ TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class);
+ ts.clear();
+ tt.getTerminals(e, ts);
+ for (Terminal t : ts) {
+ if (t instanceof ResourceTerminal) {
+ ResourceTerminal rt = (ResourceTerminal) t;
+ if (bc.terminal.equals(rt.getResource())) {
+ result.add(new Connection(connectionElement, bc.end, e, t));
+ break;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void setListener(ConnectionListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[resource=" + connection + ", connectionElement=" + getConnection()
+ + "]";
+ }
+
+ }
+