1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
\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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.sysdyn.ui.elements.connections;
\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
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
93 * An element class for Sysdyn Flow elements.
\r
94 * Copied from RouteGraphConnectionClassFactory and adapted to Flow needs
\r
96 * @author Teemu Lempinen
\r
99 public class RouteFlowConnectionFactory extends SyncElementFactory {
\r
101 public static final ElementClass CLASS = RouteFlowEdgeClass.FLOW_CLASS;
\r
104 DiagramResource DIA;
\r
105 StructuralResource2 STR;
\r
106 ModelingResources MOD;
\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
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
121 protected Resource getElementClassBaseType(AsyncReadGraph graph) {
\r
122 return graph.getService(SysdynResource.class).FlowConnection;
\r
126 public void load(ReadGraph graph, final ICanvasContext canvas, final IDiagram diagram, final Resource connection,
\r
127 final IElement element) throws DatabaseException {
\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
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
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
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
153 color = new Color(rgb.red, rgb.green, rgb.blue);
\r
154 element.setHint(ElementHints.KEY_TEXT_COLOR, color);
\r
161 RouteGraph rg = new RouteGraph();
\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
167 // Needed to support ConnectionEntity#getTerminalConnections
\r
168 Set<BackendConnection> backendonnections = new HashSet<BackendConnection>();
\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
178 nodes.add( interiorNode );
\r
179 nodeByData.put( interiorNode, line );
\r
181 for (Resource connectedTo : graph.getObjects(interiorNode, DIA.AreConnected)) {
\r
182 links.add( new EdgeResource(interiorNode, connectedTo) );
\r
184 } else if (graph.isInstanceOf(interiorNode, DIA.RoutePoint)) {
\r
185 // Not supported yet. Ignore.
\r
189 Rectangle2D bounds = new Rectangle2D.Double();
\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
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
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
207 Resource connectionRelation = graph.getInverse(terminalStm.getPredicate());
\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
218 + NameUtils.getSafeName(graph, terminalElement, true));
\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
226 //System.out.println("terminalStm: " + NameUtils.toString(graph, terminalStm));
\r
227 AffineTransform terminalElementTr = getWorldTransform(graph, terminalElement);
\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
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
241 //System.out.println("modeling rules attachment: " + NameUtils.getSafeLabel(graph, att));
\r
242 attachmentRelation = att;
\r
245 //System.out.println("attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
\r
247 // Get element bounds to decide allowed terminal direction(s)
\r
248 IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));
\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
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
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
272 x = bounds.getCenterX();
\r
273 y = bounds.getCenterY();
\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
278 //GeometryUtils.expandRectangle(bounds, 4);
\r
280 minx = bounds.getMinX();
\r
281 miny = bounds.getMinY();
\r
282 maxx = bounds.getMaxX();
\r
283 maxy = bounds.getMaxY();
\r
285 Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER);
\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
292 allowedDirections = 5; // Directions left and right (0101)
\r
295 if (allowedDirections != null) {
\r
296 direction |= allowedDirections;
\r
298 direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds);
\r
301 backendonnections.add(
\r
302 new BackendConnection(
\r
303 toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin),
\r
308 if (direction == 0)
\r
309 // Accept any horizontal/vertical direction if nothing is defined
\r
312 //System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));
\r
313 ILineEndStyle endStyle = loadLineEndStyle(graph, te, attachmentRelation, color, lw);
\r
315 RouteTerminal routeTerminal = rg.addBigTerminal(/*x, y,*/ minx, miny, maxx, maxy, /*direction,*/ endStyle);
\r
316 routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) );
\r
318 nodes.add( connector );
\r
319 nodeByData.put( connector, routeTerminal );
\r
321 for (Resource connectedTo : graph.getObjects(connector, DIA.AreConnected)) {
\r
322 links.add( new EdgeResource(connectedTo, connector) );
\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
337 // Load connection line style
\r
338 ConnectionStyle style = readConnectionStyle(graph, modelingRules, connection, element);
\r
339 StyledRouteGraphRenderer renderer = new StyledRouteGraphRenderer(style);
\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
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
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
355 public void routeGraphChanged(RouteGraphChangeEvent event) {
\r
356 scheduleSynchronize(session, connection, event);
\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
366 public void exception(ReadGraph graph, Throwable throwable) {
\r
367 throwable.printStackTrace();
\r
371 public void execute(ReadGraph graph, final Resource property) {
\r
373 ready.incrementAndGet();
\r
376 l0 = Layer0.getInstance(graph.getSession());
\r
377 } catch (DatabaseException e) {
\r
378 e.printStackTrace();
\r
382 graph.forIsSubrelationOf(property, l0.HasProperty, new AsyncProcedure<Boolean>() {
\r
385 public void exception(AsyncReadGraph graph, Throwable throwable) {
\r
386 throwable.printStackTrace();
\r
390 public void execute(AsyncReadGraph graph, final Boolean isProperty) {
\r
394 graph.forPossibleRelatedValue(connection, property, new AsyncProcedure<Object>() {
\r
397 public void exception(AsyncReadGraph graph, Throwable throwable) {
\r
398 throwable.printStackTrace();
\r
402 public void execute(AsyncReadGraph graph, final Object value) {
\r
406 l0 = Layer0.getInstance(graph.getSession());
\r
407 } catch (DatabaseException e) {
\r
408 e.printStackTrace();
\r
412 graph.forPossibleRelatedValue(property, l0.HasName, Bindings.STRING, new AsyncProcedure<String>() {
\r
415 public void exception(AsyncReadGraph graph, Throwable throwable) {
\r
416 throwable.printStackTrace();
\r
420 public void execute(AsyncReadGraph graph, String name) {
\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
438 if(ready.decrementAndGet() == 0) {
\r
439 element.setHint(DiagramHints.PROPERTIES, new HashMap<String, Pair<Resource, Object>>(properties));
\r
450 public void finished(ReadGraph graph) {
\r
452 if(ready.decrementAndGet() == 0) {
\r
453 element.setHint(DiagramHints.PROPERTIES, new HashMap<String, Object>(properties));
\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
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
479 ConnectionVisuals cv = null;
\r
480 if (connectionType != null)
\r
481 cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType),
\r
482 TransientCacheListener.<ConnectionVisuals> instance());
\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
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
493 return new FlowConnectionStyle(
\r
502 * @param connection
\r
504 * @return connection relation statement from diagram connection connector
\r
506 * @throws DatabaseException
\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
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
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
529 style = new FlowArrowLineStyle("none 0 0 0", color);
\r
539 * @throws DatabaseException
\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
545 Resource parentComponent = graph.getPossibleObject(element, MOD.HasParentComponent);
\r
546 if (parentComponent == null)
\r
548 element = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);
\r
549 if (element == null)
\r
551 AffineTransform tr = DiagramGraphUtil.getAffineTransform(graph, element);
\r
552 tr.setToTranslation(tr.getTranslateX(), tr.getTranslateY());
\r
553 result.preConcatenate(tr);
\r
558 protected void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) {
\r
559 session.asyncRequest(RouteGraphConnection.synchronizer(connection, event));
\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
566 * @see ElementHints#KEY_CONNECTION_ENTITY
\r
568 static class CE implements ConnectionEntity {
\r
571 private IDiagram diagram;
\r
573 private transient DataElementMap dataMap;
\r
576 * The connection instance resource in the graph backend.
\r
578 final Resource connection;
\r
581 * The connection entity element which is a part of the diagram.
\r
583 IElement connectionElement;
\r
586 * @see #getTerminalConnections(Collection)
\r
588 final Set<BackendConnection> backendConnections;
\r
593 Set<Connection> terminalConnections;
\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
606 this.connectionElement = ce;
\r
609 public IElement getConnection0() {
\r
610 IElement connectionElement = dataMap.getElement(diagram, connection);
\r
611 return connectionElement;
\r
615 public IElement getConnection() {
\r
616 IElement c = getConnection0();
\r
618 c = this.connectionElement;
\r
622 public Object getConnectionObject() {
\r
626 public IElement getConnectionElement() {
\r
627 return connectionElement;
\r
631 public Collection<IElement> getBranchPoints(Collection<IElement> result) {
\r
632 return result != null ? result : Collections.<IElement> emptyList();
\r
636 public Collection<IElement> getSegments(Collection<IElement> result) {
\r
637 return result != null ? result : Collections.<IElement> emptyList();
\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
647 result.addAll(terminalConnections);
\r
648 return terminalConnections;
\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
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
675 public void setListener(ConnectionListener listener) {
\r
676 throw new UnsupportedOperationException();
\r
680 public String toString() {
\r
681 return getClass().getSimpleName() + "[resource=" + connection + ", connectionElement=" + connectionElement
\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
697 this.terminal = terminal;
\r
700 public boolean equals(Object obj) {
\r
703 if (!(obj instanceof Connection))
\r
705 Connection other = (Connection) obj;
\r
706 return other.terminal == terminal
\r
707 && other.node == node
\r
708 && other.end == end;
\r
711 public int hashCode() {
\r
712 final int prime = 31;
\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
720 public String toString() {
\r
721 return "BackendConnection[node=" + node + ", terminal=" + terminal + ", end=" + end + "]";
\r