]> gerrit.simantics Code Review - simantics/sysdyn.git/blob
1b80dcf8b523b0d359694a897a847b499b2fa1c4
[simantics/sysdyn.git] /
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.sysdyn.ui.elements.connections;\r
13 \r
14 import java.awt.BasicStroke;\r
15 import java.awt.Color;\r
16 import java.awt.Shape;\r
17 import java.awt.Stroke;\r
18 import java.awt.geom.AffineTransform;\r
19 import java.awt.geom.Rectangle2D;\r
20 import java.util.ArrayList;\r
21 import java.util.Collection;\r
22 import java.util.Collections;\r
23 import java.util.HashMap;\r
24 import java.util.HashSet;\r
25 import java.util.Map;\r
26 import java.util.Set;\r
27 import java.util.concurrent.ConcurrentSkipListMap;\r
28 import java.util.concurrent.atomic.AtomicInteger;\r
29 \r
30 import org.eclipse.jface.resource.StringConverter;\r
31 import org.eclipse.swt.graphics.RGB;\r
32 import org.simantics.databoard.Bindings;\r
33 import org.simantics.db.AsyncReadGraph;\r
34 import org.simantics.db.ReadGraph;\r
35 import org.simantics.db.Resource;\r
36 import org.simantics.db.Session;\r
37 import org.simantics.db.Statement;\r
38 import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
39 import org.simantics.db.common.utils.NameUtils;\r
40 import org.simantics.db.exception.DatabaseException;\r
41 import org.simantics.db.procedure.AsyncProcedure;\r
42 import org.simantics.db.procedure.SyncMultiProcedure;\r
43 import org.simantics.diagram.G2DUtils;\r
44 import org.simantics.diagram.adapter.SyncElementFactory;\r
45 import org.simantics.diagram.connection.ConnectionVisuals;\r
46 import org.simantics.diagram.connection.RouteGraph;\r
47 import org.simantics.diagram.connection.RouteGraphConnectionClass;\r
48 import org.simantics.diagram.connection.RouteLine;\r
49 import org.simantics.diagram.connection.RouteNode;\r
50 import org.simantics.diagram.connection.RouteTerminal;\r
51 import org.simantics.diagram.connection.rendering.ConnectionStyle;\r
52 import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;\r
53 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;\r
54 import org.simantics.diagram.content.EdgeResource;\r
55 import org.simantics.diagram.content.ResourceTerminal;\r
56 import org.simantics.diagram.content.TerminalMap;\r
57 import org.simantics.diagram.query.DiagramRequests;\r
58 import org.simantics.diagram.stubs.DiagramResource;\r
59 import org.simantics.diagram.stubs.G2DResource;\r
60 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
61 import org.simantics.diagram.synchronization.graph.RouteGraphConnection;\r
62 import org.simantics.diagram.ui.DiagramModelHints;\r
63 import org.simantics.g2d.canvas.ICanvasContext;\r
64 import org.simantics.g2d.connection.ConnectionEntity;\r
65 import org.simantics.g2d.diagram.DiagramHints;\r
66 import org.simantics.g2d.diagram.IDiagram;\r
67 import org.simantics.g2d.diagram.handler.DataElementMap;\r
68 import org.simantics.g2d.diagram.handler.Topology.Connection;\r
69 import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
70 import org.simantics.g2d.element.ElementClass;\r
71 import org.simantics.g2d.element.ElementHints;\r
72 import org.simantics.g2d.element.ElementUtils;\r
73 import org.simantics.g2d.element.IElement;\r
74 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
75 import org.simantics.g2d.element.handler.TerminalTopology;\r
76 import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;\r
77 import org.simantics.g2d.routing.algorithm2.Router4;\r
78 import org.simantics.g2d.utils.TopologicalSelectionExpander;\r
79 import org.simantics.layer0.Layer0;\r
80 import org.simantics.modeling.ModelingResources;\r
81 import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;\r
82 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;\r
83 import org.simantics.structural.stubs.StructuralResource2;\r
84 import org.simantics.structural2.modelingRules.CPTerminal;\r
85 import org.simantics.structural2.modelingRules.IAttachmentRelationMap;\r
86 import org.simantics.structural2.modelingRules.IModelingRules;\r
87 import org.simantics.sysdyn.SysdynResource;\r
88 import org.simantics.sysdyn.ui.elements.ValveFactory.ValveSceneGraph;\r
89 import org.simantics.sysdyn.ui.preferences.SysdynDiagramPreferences;\r
90 import org.simantics.sysdyn.ui.preferences.SysdynDiagramPropertyExternalRead;\r
91 import org.simantics.utils.datastructures.Pair;\r
92 /**\r
93  * An element class for Sysdyn Flow elements.\r
94  * Copied from RouteGraphConnectionClassFactory and adapted to Flow needs\r
95  * \r
96  * @author Teemu Lempinen\r
97  * \r
98  */\r
99 public class RouteFlowConnectionFactory extends SyncElementFactory {\r
100 \r
101     public static final ElementClass CLASS = RouteFlowEdgeClass.FLOW_CLASS;\r
102 \r
103     Layer0                             L0;\r
104     DiagramResource                    DIA;\r
105     StructuralResource2                STR;\r
106     ModelingResources                  MOD;\r
107 \r
108     public RouteFlowConnectionFactory(ReadGraph graph) {\r
109         this.L0 = Layer0.getInstance(graph);\r
110         this.DIA = DiagramResource.getInstance(graph);\r
111         this.STR = StructuralResource2.getInstance(graph);\r
112         this.MOD = ModelingResources.getInstance(graph);\r
113     }\r
114 \r
115     @Override\r
116     public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, final AsyncProcedure<ElementClass> procedure) {\r
117         procedure.execute(graph, CLASS.newClassWith(false, new StaticObjectAdapter(elementType)));\r
118     }\r
119 \r
120     @Override\r
121     protected Resource getElementClassBaseType(AsyncReadGraph graph) {\r
122         return graph.getService(SysdynResource.class).FlowConnection;\r
123     }\r
124 \r
125     @Override\r
126     public void load(ReadGraph graph, final ICanvasContext canvas, final IDiagram diagram, final Resource connection,\r
127             final IElement element) throws DatabaseException {\r
128 \r
129         // Do we need this?\r
130         element.setHint(DiagramHints.ROUTE_ALGORITHM, new Router4(false));\r
131         System.out.println(connection);\r
132         IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);\r
133 \r
134 //        IElement mappedElement = ElementUtils.getByData(diagram, connection);\r
135 //        if (mappedElement == null) {\r
136 //            System.out.println("taalla ollaan mappedElement == null");\r
137 //            // FIXME: With undo this seems to happen, don't know why yet!\r
138 //            return;\r
139 //        }\r
140 \r
141         Color color = null;\r
142         DiagramResource DR = DiagramResource.getInstance(graph);\r
143         G2DResource G2D = G2DResource.getInstance(graph);\r
144         if (graph.isInstanceOf(connection, DR.ColorProvider)) {\r
145             Statement colorStatement = graph.getPossibleStatement(connection, G2D.HasColor);\r
146             if(colorStatement != null && !colorStatement.isAsserted(connection)) {\r
147                 element.setHint(ElementHints.KEY_TEXT_COLOR, G2DUtils.getColor(graph, colorStatement.getObject()));\r
148             } else {\r
149                 String colorString = graph.syncRequest(new SysdynDiagramPropertyExternalRead(new Pair<Resource, String>(connection, SysdynDiagramPreferences.getColorPreferenceName(graph, connection))));\r
150                 if(colorString != null) {\r
151                     RGB rgb = StringConverter.asRGB(colorString, null);\r
152                     if(rgb != null) {\r
153                         color = new Color(rgb.red, rgb.green, rgb.blue);\r
154                         element.setHint(ElementHints.KEY_TEXT_COLOR, color);\r
155                     }\r
156                 }\r
157 \r
158             }\r
159         }\r
160 \r
161         RouteGraph rg = new RouteGraph();\r
162 \r
163         Set<Resource> nodes = new HashSet<Resource>();\r
164         Set<EdgeResource> links = new HashSet<EdgeResource>();\r
165         Map<Object, RouteNode> nodeByData = new HashMap<Object, RouteNode>();\r
166 \r
167         // Needed to support ConnectionEntity#getTerminalConnections\r
168         Set<BackendConnection> backendonnections = new HashSet<BackendConnection>();\r
169 \r
170         // Load all route graph interior RouteNodes: route lines and points\r
171         for (Resource interiorNode : graph.getObjects(connection, DIA.HasInteriorRouteNode)) {\r
172             if (graph.isInstanceOf(interiorNode, DIA.RouteLine)) {\r
173                 Boolean isHorizontal = graph.getRelatedValue(interiorNode, DIA.IsHorizontal, Bindings.BOOLEAN);\r
174                 Double position = graph.getRelatedValue(interiorNode, DIA.HasPosition, Bindings.DOUBLE);\r
175                 RouteLine line = rg.addLine(isHorizontal, position);\r
176                 line.setData( RouteGraphConnection.serialize(graph, interiorNode) );\r
177 \r
178                 nodes.add( interiorNode );\r
179                 nodeByData.put( interiorNode, line );\r
180 \r
181                 for (Resource connectedTo : graph.getObjects(interiorNode, DIA.AreConnected)) {\r
182                     links.add( new EdgeResource(interiorNode, connectedTo) );\r
183                 }\r
184             } else if (graph.isInstanceOf(interiorNode, DIA.RoutePoint)) {\r
185                 // Not supported yet. Ignore.\r
186             }\r
187         }\r
188 \r
189         Rectangle2D bounds = new Rectangle2D.Double();\r
190 \r
191         // Load all node terminal connections as RouteTerminals\r
192         for (Statement toConnector : graph.getStatements(connection, DIA.HasConnector)) {\r
193             Resource connector = toConnector.getObject();\r
194             Resource attachmentRelation = toConnector.getPredicate();\r
195 \r
196             Statement terminalStm = findTerminalStatement(graph, STR, connection, connector);\r
197             if (terminalStm == null)\r
198                 // Ignore broken connector: attached to the connection but not to any terminal.\r
199                 continue;\r
200 \r
201             Resource terminalElement = terminalStm.getObject();\r
202             Resource terminalElementType = graph.getPossibleType(terminalElement, DIA.Element);\r
203             if (terminalElementType == null)\r
204                 // Ignore non-element terminal elements\r
205                 continue;\r
206 \r
207             Resource connectionRelation = graph.getInverse(terminalStm.getPredicate());\r
208 \r
209             // Discover node and terminal this connector is connected to.\r
210             TerminalMap terminals = graph.syncRequest(DiagramRequests.elementTypeTerminals(terminalElementType),\r
211                     TransientCacheListener.<TerminalMap> instance());\r
212             Resource terminal = terminals.getTerminal(connectionRelation);\r
213             if (terminal == null) {\r
214                 System.err.println(getClass().getSimpleName()\r
215                         + ": Could not find terminal for connection point "\r
216                         + NameUtils.getSafeName(graph, connectionRelation, true)\r
217                         + " in element "\r
218                         + NameUtils.getSafeName(graph, terminalElement, true)); \r
219                 continue;\r
220             }\r
221 \r
222             double[] position = graph.getRelatedValue(connector, DIA.HasRelativeLocation, Bindings.DOUBLE_ARRAY);\r
223             if (position.length != 2)\r
224                 position = new double[] { 0, 0 };\r
225 \r
226             //System.out.println("terminalStm: " + NameUtils.toString(graph, terminalStm));\r
227             AffineTransform terminalElementTr = getWorldTransform(graph, terminalElement);\r
228 \r
229             double x = terminalElementTr.getTranslateX();\r
230             double y = terminalElementTr.getTranslateY();\r
231             double minx = x-1, miny = y-1, maxx = x+1, maxy = y+1;\r
232             int direction = 0x0;\r
233 \r
234             // Use modelingRules to ascertain the proper attachmentRelation\r
235             // for this terminal connection, if available.\r
236             if (modelingRules != null) {\r
237                 // Get attachmentRelation from modelingRules if possible.\r
238                 IAttachmentRelationMap map = modelingRules.getAttachmentRelations(graph, connection);\r
239                 Resource att = map.get(graph, new CPTerminal(terminalElement, terminal));\r
240                 if (att != null) {\r
241                     //System.out.println("modeling rules attachment: " + NameUtils.getSafeLabel(graph, att));\r
242                     attachmentRelation = att;\r
243                 }\r
244             }\r
245             //System.out.println("attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
246 \r
247             // Get element bounds to decide allowed terminal direction(s)\r
248             IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));\r
249   \r
250             // Fetch the flow width\r
251             float lw = FlowConnectionStyle.DEFAULT_LINE_WIDTH;\r
252             SysdynResource sr = SysdynResource.getInstance(graph);\r
253                 Float width = graph.getPossibleRelatedValue(connection, sr.FlowConnection_width, Bindings.FLOAT);\r
254                 if (width != null)\r
255                         lw = width;\r
256             \r
257                 if(te.getElementClass().containsClass(ValveSceneGraph.class)) {\r
258                 // Valve behaves differently. The flow must start inside the valve bounds\r
259                 ValveSceneGraph vs = te.getElementClass().getSingleItem(ValveSceneGraph.class);\r
260                 Rectangle2D size = new Rectangle2D.Double();\r
261                 vs.getValveBounds(te, size);\r
262                 Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(size, terminalElementTr);\r
263                 size = (Rectangle2D) shp;\r
264                 bounds.setFrame(new Rectangle2D.Double(size.getCenterX() - (lw/2), size.getCenterY() - (lw/2), lw, lw));\r
265             } else {\r
266                 // Basic bounds\r
267                 bounds = ElementUtils.getElementShape(te).getBounds2D();\r
268                 Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalElementTr);\r
269                 bounds.setFrame(shp.getBounds2D());\r
270             }\r
271             \r
272             x = bounds.getCenterX();\r
273             y = bounds.getCenterY();\r
274 \r
275             // Expand bounds by 4mm to make the connections enter the terminals\r
276             // at a straight angle and from a distance instead of coming in\r
277             // "horizontally".\r
278             //GeometryUtils.expandRectangle(bounds, 4);\r
279 \r
280             minx = bounds.getMinX();\r
281             miny = bounds.getMinY();\r
282             maxx = bounds.getMaxX();\r
283             maxy = bounds.getMaxY();\r
284 \r
285             Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER);\r
286 \r
287             // Valve behaves differently. Allowed directions depend on the orientation of the valve\r
288             if(te.getElementClass().containsClass(ValveSceneGraph.class)) {\r
289                 if(graph.hasStatement(terminalElement, sr.ValveSymbol_orientation, sr.Vertical)) {\r
290                     allowedDirections = 10; // Directions up and down (1010)\r
291                 } else {\r
292                     allowedDirections = 5; // Directions left and right (0101)\r
293                 }\r
294             }\r
295             if (allowedDirections != null) {\r
296                 direction |= allowedDirections;\r
297             } else {\r
298                 direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds);\r
299             }\r
300 \r
301             backendonnections.add(\r
302                     new BackendConnection(\r
303                             toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin),\r
304                             terminalElement,\r
305                             terminal)\r
306                     );\r
307 \r
308             if (direction == 0)\r
309                 // Accept any horizontal/vertical direction if nothing is defined\r
310                 direction = 0xf;\r
311 \r
312             //System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));\r
313             ILineEndStyle endStyle = loadLineEndStyle(graph, te, attachmentRelation, color, lw);\r
314 \r
315             RouteTerminal routeTerminal = rg.addBigTerminal(/*x, y,*/ minx, miny, maxx, maxy, /*direction,*/ endStyle);\r
316             routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) );\r
317 \r
318             nodes.add( connector );\r
319             nodeByData.put( connector, routeTerminal );\r
320 \r
321             for (Resource connectedTo : graph.getObjects(connector, DIA.AreConnected)) {\r
322                 links.add( new EdgeResource(connectedTo, connector) );\r
323             }\r
324         }\r
325 \r
326         // Finish route graph loading by Linking route nodes together\r
327         for (EdgeResource link : links) {\r
328             RouteNode n1 = nodeByData.get(link.first());\r
329             RouteNode n2 = nodeByData.get(link.second());\r
330             if (n1 == null || n2 == null) {\r
331                 System.err.println("Stray connection link found: " + link.toString(graph));\r
332                 continue;\r
333             }\r
334             rg.link(n1, n2);\r
335         }\r
336 \r
337         // Load connection line style\r
338         ConnectionStyle style = readConnectionStyle(graph, modelingRules, connection, element);\r
339         StyledRouteGraphRenderer renderer = new StyledRouteGraphRenderer(style);\r
340 \r
341         // Finish element load\r
342         element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg);\r
343         element.setHint(RouteGraphConnectionClass.KEY_RENDERER, renderer);\r
344         element.setHint(RouteGraphConnectionClass.KEY_PICK_TOLERANCE, 0.5);\r
345 \r
346         // Initialize ConnectionEntity in element\r
347         // NOTE: MUST use the mapped element with class CE, not the connection (element) were loading into.\r
348         // GDS will synchronize element into mappedElement in a controlled manner.\r
349         element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(diagram, connection, element, backendonnections));\r
350 \r
351         // Setup graph writeback support for route graph modifications\r
352         final Session session = graph.getSession();\r
353         element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() {\r
354             @Override\r
355             public void routeGraphChanged(RouteGraphChangeEvent event) {\r
356                 scheduleSynchronize(session, connection, event);\r
357             }\r
358         });\r
359         \r
360      // A complicated-looking procedure for obtaining all HasProperties to properties map\r
361         final AtomicInteger ready = new AtomicInteger(1);\r
362         final ConcurrentSkipListMap<String, Pair<Resource, Object>> properties = new ConcurrentSkipListMap<String, Pair<Resource, Object>>();\r
363         graph.forEachPredicate(connection, new SyncMultiProcedure<Resource>() {\r
364 \r
365             @Override\r
366             public void exception(ReadGraph graph, Throwable throwable) {\r
367                 throwable.printStackTrace();\r
368             }\r
369 \r
370             @Override\r
371             public void execute(ReadGraph graph, final Resource property) {\r
372 \r
373                 ready.incrementAndGet();\r
374                 Layer0 l0;\r
375                 try {\r
376                     l0 = Layer0.getInstance(graph.getSession());\r
377                 } catch (DatabaseException e) {\r
378                     e.printStackTrace();\r
379                     return;\r
380                 }\r
381 \r
382                 graph.forIsSubrelationOf(property, l0.HasProperty, new AsyncProcedure<Boolean>() {\r
383 \r
384                     @Override\r
385                     public void exception(AsyncReadGraph graph, Throwable throwable) {\r
386                         throwable.printStackTrace();\r
387                     }\r
388 \r
389                     @Override\r
390                     public void execute(AsyncReadGraph graph, final Boolean isProperty) {\r
391 \r
392                         if(isProperty) {\r
393 \r
394                             graph.forPossibleRelatedValue(connection, property, new AsyncProcedure<Object>() {\r
395 \r
396                                 @Override\r
397                                 public void exception(AsyncReadGraph graph, Throwable throwable) {\r
398                                     throwable.printStackTrace();\r
399                                 }\r
400 \r
401                                 @Override\r
402                                 public void execute(AsyncReadGraph graph, final Object value) {\r
403 \r
404                                     Layer0 l0;\r
405                                     try {\r
406                                         l0 = Layer0.getInstance(graph.getSession());\r
407                                     } catch (DatabaseException e) {\r
408                                         e.printStackTrace();\r
409                                         return;\r
410                                     }\r
411 \r
412                                     graph.forPossibleRelatedValue(property, l0.HasName, Bindings.STRING, new AsyncProcedure<String>() {\r
413 \r
414                                         @Override\r
415                                         public void exception(AsyncReadGraph graph, Throwable throwable) {\r
416                                             throwable.printStackTrace();\r
417                                         }\r
418 \r
419                                         @Override\r
420                                         public void execute(AsyncReadGraph graph, String name) {\r
421 \r
422                                             properties.put(name, Pair.make(property, value));\r
423                                             if(ready.decrementAndGet() == 0) {\r
424                                                 element.setHint(DiagramHints.PROPERTIES, new HashMap<String, Pair<Resource, Object>>(properties));\r
425                                             }\r
426 \r
427                                         }\r
428 \r
429                                     });\r
430 \r
431                                 }\r
432 \r
433                             });\r
434 \r
435 \r
436                         } else {\r
437 \r
438                             if(ready.decrementAndGet() == 0) {\r
439                                 element.setHint(DiagramHints.PROPERTIES, new HashMap<String, Pair<Resource, Object>>(properties));\r
440                             }\r
441 \r
442                         }\r
443 \r
444                     }\r
445 \r
446                 });\r
447             }\r
448 \r
449             @Override\r
450             public void finished(ReadGraph graph) {\r
451 \r
452                 if(ready.decrementAndGet() == 0) {\r
453                     element.setHint(DiagramHints.PROPERTIES, new HashMap<String, Object>(properties));\r
454                 }\r
455 \r
456             }\r
457 \r
458         });\r
459 \r
460     }\r
461 \r
462     private EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue)\r
463             throws DatabaseException {\r
464         if (graph.isSubrelationOf(attachmentRelation, DIA.IsTailConnectorOf))\r
465             return EdgeEnd.Begin;\r
466         if (graph.isSubrelationOf(attachmentRelation, DIA.IsHeadConnectorOf))\r
467             return EdgeEnd.End;\r
468         return defaultValue;\r
469     }\r
470 \r
471     private ConnectionStyle readConnectionStyle(ReadGraph graph, IModelingRules modelingRules, Resource connection,\r
472             IElement element) throws DatabaseException {\r
473         Resource connectionType = null;\r
474         if (modelingRules != null)\r
475             connectionType = modelingRules.getConnectionType(graph, connection);\r
476         if (connectionType == null)\r
477             connectionType = graph.getPossibleObject(connection, STR.HasConnectionType);\r
478 \r
479         ConnectionVisuals cv = null;\r
480         if (connectionType != null)\r
481             cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType),\r
482                     TransientCacheListener.<ConnectionVisuals> instance());\r
483 \r
484 \r
485         Color lineColor = element.getHint(ElementHints.KEY_TEXT_COLOR);\r
486         if (lineColor == null)\r
487             lineColor = (cv != null && cv.toColor() != null) ? cv.toColor() : Color.DARK_GRAY;\r
488 \r
489             Stroke lineStroke = cv != null ? cv.stroke : null;\r
490             if (lineStroke == null)\r
491                 lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0);\r
492 \r
493             return new FlowConnectionStyle(\r
494                     lineColor,\r
495                     lineStroke,\r
496                     connection);\r
497     }\r
498 \r
499     /**\r
500      * @param graph\r
501      * @param STR\r
502      * @param connection\r
503      * @param connector\r
504      * @return connection relation statement from diagram connection connector\r
505      *         to a node\r
506      * @throws DatabaseException\r
507      */\r
508     private static Statement findTerminalStatement(ReadGraph graph, StructuralResource2 STR, Resource connection,\r
509             Resource connector) throws DatabaseException {\r
510         for (Statement stm : graph.getStatements(connector, STR.Connects)) {\r
511             if (connection.equals(stm.getObject()))\r
512                 continue;\r
513             return stm;\r
514         }\r
515         return null;\r
516     }\r
517 \r
518     public ILineEndStyle loadLineEndStyle(ReadGraph graph, IElement te, Resource attachmentRelation, Color color, float lineWidth)\r
519             throws DatabaseException {\r
520         ILineEndStyle style;\r
521         // TODO: change bounds according to terminal type: Very small rectangle for Valves, Text box size for Stocks and Clouds\r
522         if(te.getElementClass().containsClass(ValveSceneGraph.class)) {\r
523             style =  new FlowArrowLineStyle("none 0 0 0", color);\r
524         } else {\r
525             if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) {\r
526                 float arrowSize = lineWidth * 1.3f;\r
527                 style = new FlowArrowLineStyle("fill " + arrowSize + " " + arrowSize + " 0", color);\r
528             } else {\r
529                 style =  new FlowArrowLineStyle("none 0 0 0", color);\r
530             }\r
531         }\r
532         return style;\r
533     }\r
534 \r
535     /**\r
536      * @param graph\r
537      * @param element\r
538      * @return\r
539      * @throws DatabaseException\r
540      */\r
541     private static AffineTransform getWorldTransform(ReadGraph graph, Resource element) throws DatabaseException {\r
542         ModelingResources MOD = ModelingResources.getInstance(graph);\r
543         AffineTransform result = DiagramGraphUtil.getAffineTransform(graph, element);\r
544         while (true) {\r
545             Resource parentComponent = graph.getPossibleObject(element, MOD.HasParentComponent);\r
546             if (parentComponent == null)\r
547                 return result;\r
548             element = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);\r
549             if (element == null)\r
550                 return result;\r
551             AffineTransform tr = DiagramGraphUtil.getAffineTransform(graph, element);\r
552             tr.setToTranslation(tr.getTranslateX(), tr.getTranslateY());\r
553             result.preConcatenate(tr);\r
554         }\r
555     }\r
556 \r
557 \r
558     protected void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) {\r
559         session.asyncRequest(RouteGraphConnection.synchronizer(connection, event));\r
560     }\r
561 \r
562     /**\r
563      * Must have this in order for {@link TopologicalSelectionExpander} to work.\r
564      * Otherwise this is pretty useless and should be deprecated altogether.\r
565      * \r
566      * @see ElementHints#KEY_CONNECTION_ENTITY\r
567      */\r
568     static class CE implements ConnectionEntity {\r
569 \r
570         \r
571         private IDiagram diagram;\r
572 \r
573         private transient DataElementMap dataMap;\r
574         \r
575         /**\r
576          * The connection instance resource in the graph backend.\r
577          */\r
578         final Resource               connection;\r
579 \r
580         /**\r
581          * The connection entity element which is a part of the diagram.\r
582          */\r
583         IElement               connectionElement;\r
584 \r
585         /**\r
586          * @see #getTerminalConnections(Collection)\r
587          */\r
588         final Set<BackendConnection> backendConnections;\r
589 \r
590         /**\r
591          * Cache.\r
592          */\r
593         Set<Connection>              terminalConnections;\r
594 \r
595         \r
596         public CE(IDiagram diagram, Resource connection, IElement connectionElement, Set<BackendConnection> backendConnections) {\r
597             if (connectionElement == null)\r
598                 throw new NullPointerException("null connection element");\r
599             this.diagram = diagram;\r
600             this.dataMap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
601             this.connection = connection;\r
602             this.connectionElement = connectionElement;\r
603             this.backendConnections = backendConnections;\r
604             IElement ce = getConnection0();\r
605             if (ce != null)\r
606                 this.connectionElement = ce;\r
607         }\r
608 \r
609         public IElement getConnection0() {\r
610             IElement connectionElement = dataMap.getElement(diagram, connection);\r
611             return connectionElement;\r
612         }\r
613 \r
614         @Override\r
615         public IElement getConnection() {\r
616             IElement c = getConnection0();\r
617             if (c == null)\r
618                 c = this.connectionElement;\r
619             return c;\r
620         }\r
621 \r
622         public Object getConnectionObject() {\r
623             return connection;\r
624         }\r
625 \r
626         public IElement getConnectionElement() {\r
627             return connectionElement;\r
628         }\r
629 \r
630         @Override\r
631         public Collection<IElement> getBranchPoints(Collection<IElement> result) {\r
632             return result != null ? result : Collections.<IElement> emptyList();\r
633         }\r
634 \r
635         @Override\r
636         public Collection<IElement> getSegments(Collection<IElement> result) {\r
637             return result != null ? result : Collections.<IElement> emptyList();\r
638         }\r
639 \r
640         @Override\r
641         public Collection<Connection> getTerminalConnections(Collection<Connection> result) {\r
642             if (terminalConnections == null)\r
643                 terminalConnections = calculateTerminalConnections();\r
644             if (result == null)\r
645                 result = new ArrayList<Connection>(terminalConnections);\r
646             else\r
647                 result.addAll(terminalConnections);\r
648             return terminalConnections;\r
649         }\r
650 \r
651         private Set<Connection> calculateTerminalConnections() {\r
652             DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
653             Set<Connection> result = new HashSet<Connection>();\r
654             ArrayList<Terminal> ts = new ArrayList<Terminal>();\r
655             for (BackendConnection bc : backendConnections) {\r
656                 IElement e = dem.getElement(diagram, bc.node);\r
657                 if (e == null)\r
658                     continue;\r
659                 TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class);\r
660                 tt.getTerminals(e, ts);\r
661                 for (Terminal t : ts) {\r
662                     if (t instanceof ResourceTerminal) {\r
663                         ResourceTerminal rt = (ResourceTerminal) t;\r
664                         if (bc.terminal.equals(rt.getResource())) {\r
665                             result.add(new Connection(connectionElement, bc.end, e, t));\r
666                             break;\r
667                         }\r
668                     }\r
669                 }\r
670             }\r
671             return result;\r
672         }\r
673 \r
674         @Override\r
675         public void setListener(ConnectionListener listener) {\r
676             throw new UnsupportedOperationException();\r
677         }\r
678 \r
679         @Override\r
680         public String toString() {\r
681             return getClass().getSimpleName() + "[resource=" + connection + ", connectionElement=" + connectionElement\r
682                     + "]";\r
683         }\r
684 \r
685     }\r
686 \r
687     public static class BackendConnection {\r
688         public final Resource node;\r
689         public final Resource terminal;\r
690         public final EdgeEnd  end;\r
691         public BackendConnection(EdgeEnd end, Resource node, Resource terminal) {\r
692             assert end != null;\r
693             assert node != null;\r
694             assert terminal != null;\r
695             this.end = end;\r
696             this.node = node;\r
697             this.terminal = terminal;\r
698         }\r
699         @Override\r
700         public boolean equals(Object obj) {\r
701             if (this == obj)\r
702                 return true;\r
703             if (!(obj instanceof Connection))\r
704                 return false;\r
705             Connection other = (Connection) obj;\r
706             return other.terminal == terminal\r
707                     && other.node == node\r
708                     && other.end == end;\r
709         }\r
710         @Override\r
711         public int hashCode() {\r
712             final int prime = 31;\r
713             int result = 1;\r
714             result = prime * result + end.hashCode();\r
715             result = prime * result + ((node == null) ? 0 : node.hashCode());\r
716             result = prime * result + ((terminal == null) ? 0 : terminal.hashCode());\r
717             return result;\r
718         }\r
719         @Override\r
720         public String toString() {\r
721             return "BackendConnection[node=" + node + ", terminal=" + terminal + ", end=" + end + "]";\r
722         }\r
723     }\r
724 \r
725 }\r
726 \r