RouteGraph related methods are now public for code reuse
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / adapter / RouteGraphConnectionClassFactory.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2016 Association for Decentralized Information Management\r
3  * in 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  *     Semantum Oy - refactoring\r
12  *******************************************************************************/\r
13 package org.simantics.diagram.adapter;\r
14 \r
15 import java.util.ArrayList;\r
16 import java.util.Collection;\r
17 import java.util.Collections;\r
18 import java.util.Set;\r
19 \r
20 import org.simantics.db.AsyncReadGraph;\r
21 import org.simantics.db.ReadGraph;\r
22 import org.simantics.db.Resource;\r
23 import org.simantics.db.Session;\r
24 import org.simantics.db.exception.DatabaseException;\r
25 import org.simantics.db.procedure.AsyncProcedure;\r
26 import org.simantics.diagram.adapter.RouteGraphUtils.BackendConnection;\r
27 import org.simantics.diagram.connection.RouteGraph;\r
28 import org.simantics.diagram.connection.RouteGraphConnectionClass;\r
29 import org.simantics.diagram.connection.rendering.ConnectionStyle;\r
30 import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;\r
31 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;\r
32 import org.simantics.diagram.content.ResourceTerminal;\r
33 import org.simantics.diagram.stubs.DiagramResource;\r
34 import org.simantics.diagram.ui.DiagramModelHints;\r
35 import org.simantics.g2d.canvas.ICanvasContext;\r
36 import org.simantics.g2d.connection.ConnectionEntity;\r
37 import org.simantics.g2d.diagram.IDiagram;\r
38 import org.simantics.g2d.diagram.handler.DataElementMap;\r
39 import org.simantics.g2d.diagram.handler.Topology.Connection;\r
40 import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
41 import org.simantics.g2d.element.ElementClass;\r
42 import org.simantics.g2d.element.ElementHints;\r
43 import org.simantics.g2d.element.IElement;\r
44 import org.simantics.g2d.element.handler.TerminalTopology;\r
45 import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;\r
46 import org.simantics.g2d.utils.TopologicalSelectionExpander;\r
47 import org.simantics.layer0.Layer0;\r
48 import org.simantics.modeling.ModelingResources;\r
49 import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;\r
50 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;\r
51 import org.simantics.structural.stubs.StructuralResource2;\r
52 import org.simantics.structural2.modelingRules.IModelingRules;\r
53 \r
54 import gnu.trove.set.hash.THashSet;\r
55 \r
56 /**\r
57  * An element class for single connection entity elements. A connection entity\r
58  * consists of connection edge segments and branch points as its children.\r
59  * \r
60  * @author Tuukka Lehtonen\r
61  */\r
62 public class RouteGraphConnectionClassFactory extends SyncElementFactory {\r
63 \r
64     public static final ElementClass   CLASS = RouteGraphConnectionClass.CLASS;\r
65 \r
66     public static final ILineEndStyle  HEAD  = RouteGraphUtils.HEAD;\r
67     public static final ILineEndStyle  TAIL  = RouteGraphUtils.TAIL;\r
68 \r
69     protected Layer0                   L0;\r
70     protected DiagramResource          DIA;\r
71     protected StructuralResource2      STR;\r
72     protected ModelingResources        MOD;\r
73 \r
74     public RouteGraphConnectionClassFactory(ReadGraph graph) {\r
75         this.L0 = Layer0.getInstance(graph);\r
76         this.DIA = DiagramResource.getInstance(graph);\r
77         this.STR = StructuralResource2.getInstance(graph);\r
78         this.MOD = ModelingResources.getInstance(graph);\r
79     }\r
80 \r
81     @Override\r
82     public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType,\r
83             final AsyncProcedure<ElementClass> procedure) {\r
84         procedure.execute(graph, CLASS.newClassWith(false, new StaticObjectAdapter(elementType)));\r
85     }\r
86 \r
87     @Override\r
88     protected Resource getElementClassBaseType(AsyncReadGraph graph) {\r
89         return DIA.Connection;\r
90     }\r
91 \r
92     @Override\r
93     public void load(ReadGraph graph, ICanvasContext canvas, IDiagram diagram, final Resource connection,\r
94             IElement element) throws DatabaseException {\r
95 \r
96         IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);\r
97         Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);\r
98 \r
99         Set<BackendConnection> backendConnections = new THashSet<>();\r
100         RouteGraph rg = RouteGraphUtils.load(graph, diagramRuntime, connection, canvas, diagram, modelingRules, backendConnections);\r
101 \r
102         // Load connection line style.\r
103         ConnectionStyle style = RouteGraphUtils.readConnectionStyle(graph, modelingRules, connection, STR);\r
104         StyledRouteGraphRenderer renderer = RouteGraphUtils.getRenderer(graph, style);\r
105 \r
106         // Finish element load\r
107         element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg);\r
108         element.setHint(RouteGraphConnectionClass.KEY_RENDERER, renderer);\r
109         element.setHint(RouteGraphConnectionClass.KEY_PICK_TOLERANCE, 0.5);\r
110 \r
111         // Initialize ConnectionEntity in element\r
112         element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(diagram, connection, element, backendConnections));\r
113 \r
114         // Setup graph writeback support for route graph modifications\r
115         Session session = graph.getSession();\r
116         element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() {\r
117             @Override\r
118             public void routeGraphChanged(RouteGraphChangeEvent event) {\r
119                 RouteGraphUtils.scheduleSynchronize(session, connection, event);\r
120             }\r
121         });\r
122     }\r
123 \r
124     /**\r
125      * Must have this in order for {@link TopologicalSelectionExpander} to work.\r
126      * Otherwise this is pretty useless and should be deprecated altogether.\r
127      * \r
128      * @see ElementHints#KEY_CONNECTION_ENTITY\r
129      */\r
130     public static class CE implements ConnectionEntity {\r
131 \r
132         /**\r
133          * Needed to gain access to {@link DataElementMap}.\r
134          */\r
135         final IDiagram               diagram;\r
136 \r
137         /**\r
138          * The connection instance resource in the graph database back-end.\r
139          */\r
140         final Resource               connection;\r
141 \r
142         /**\r
143          * The current element mapped to connection. \r
144          */\r
145         IElement                     connectionElement;\r
146 \r
147         /**\r
148          * @see #getTerminalConnections(Collection)\r
149          */\r
150         final Set<BackendConnection> backendConnections;\r
151 \r
152         /**\r
153          * Cache.\r
154          */\r
155         transient Set<Connection>    terminalConnections;\r
156 \r
157         public CE(IDiagram diagram, Resource connection, IElement connectionElement, Set<BackendConnection> backendConnections) {\r
158             if (connectionElement == null)\r
159                 throw new NullPointerException("null connection element");\r
160             this.diagram = diagram;\r
161             this.connection = connection;\r
162             this.connectionElement = connectionElement;\r
163             this.backendConnections = backendConnections;\r
164             IElement ce = getConnection0();\r
165             if (ce != null)\r
166                 this.connectionElement = ce;\r
167         }\r
168 \r
169         public IElement getConnection0() {\r
170             DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
171             IElement connectionElement = dem.getElement(diagram, connection);\r
172             return connectionElement;\r
173         }\r
174 \r
175         @Override\r
176         public IElement getConnection() {\r
177             IElement c = getConnection0();\r
178             if (c == null)\r
179                 c = this.connectionElement;\r
180             return c;\r
181         }\r
182 \r
183         @Override\r
184         public Collection<IElement> getBranchPoints(Collection<IElement> result) {\r
185             return result != null ? result : Collections.<IElement> emptyList();\r
186         }\r
187 \r
188         @Override\r
189         public Collection<IElement> getSegments(Collection<IElement> result) {\r
190             return result != null ? result : Collections.<IElement> emptyList();\r
191         }\r
192 \r
193         @Override\r
194         public Collection<Connection> getTerminalConnections(Collection<Connection> result) {\r
195             if (terminalConnections == null)\r
196                 terminalConnections = calculateTerminalConnections();\r
197             if (result == null)\r
198                 result = new ArrayList<Connection>(terminalConnections);\r
199             else\r
200                 result.addAll(terminalConnections);\r
201             return terminalConnections;\r
202         }\r
203 \r
204         private Set<Connection> calculateTerminalConnections() {\r
205             Set<Connection> result = new THashSet<Connection>(backendConnections.size());\r
206             DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
207             IElement connectionElement = dem.getElement(diagram, connection);\r
208             if (connectionElement == null)\r
209                 throw new NullPointerException("connection is not mapped");\r
210             ArrayList<Terminal> ts = new ArrayList<Terminal>();\r
211             for (BackendConnection bc : backendConnections) {\r
212                 IElement e = dem.getElement(diagram, bc.node);\r
213                 if (e == null)\r
214                     continue;\r
215                 TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class);\r
216                 ts.clear();\r
217                 tt.getTerminals(e, ts);\r
218                 for (Terminal t : ts) {\r
219                     if (t instanceof ResourceTerminal) {\r
220                         ResourceTerminal rt = (ResourceTerminal) t;\r
221                         if (bc.terminal.equals(rt.getResource())) {\r
222                             result.add(new Connection(connectionElement, bc.end, e, t));\r
223                             break;\r
224                         }\r
225                     }\r
226                 }\r
227             }\r
228             return result;\r
229         }\r
230 \r
231         @Override\r
232         public void setListener(ConnectionListener listener) {\r
233             throw new UnsupportedOperationException();\r
234         }\r
235 \r
236         @Override\r
237         public String toString() {\r
238             return getClass().getSimpleName() + "[resource=" + connection + ", connectionElement=" + getConnection()\r
239                     + "]";\r
240         }\r
241 \r
242     }\r
243 \r
244 }