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