1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.diagram.adapter;
\r
14 import gnu.trove.map.hash.THashMap;
\r
15 import gnu.trove.set.hash.THashSet;
\r
17 import java.awt.BasicStroke;
\r
18 import java.awt.Color;
\r
19 import java.awt.Shape;
\r
20 import java.awt.Stroke;
\r
21 import java.awt.geom.AffineTransform;
\r
22 import java.awt.geom.Rectangle2D;
\r
23 import java.util.ArrayList;
\r
24 import java.util.Collection;
\r
25 import java.util.Collections;
\r
26 import java.util.Map;
\r
27 import java.util.Set;
\r
29 import org.simantics.databoard.Bindings;
\r
30 import org.simantics.db.AsyncReadGraph;
\r
31 import org.simantics.db.ReadGraph;
\r
32 import org.simantics.db.Resource;
\r
33 import org.simantics.db.Session;
\r
34 import org.simantics.db.Statement;
\r
35 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
\r
36 import org.simantics.db.common.request.ResourceRead2;
\r
37 import org.simantics.db.common.request.UnaryRead;
\r
38 import org.simantics.db.common.utils.NameUtils;
\r
39 import org.simantics.db.exception.DatabaseException;
\r
40 import org.simantics.db.procedure.AsyncProcedure;
\r
41 import org.simantics.diagram.connection.ConnectionVisuals;
\r
42 import org.simantics.diagram.connection.RouteGraph;
\r
43 import org.simantics.diagram.connection.RouteGraphConnectionClass;
\r
44 import org.simantics.diagram.connection.RouteLine;
\r
45 import org.simantics.diagram.connection.RouteNode;
\r
46 import org.simantics.diagram.connection.RouteTerminal;
\r
47 import org.simantics.diagram.connection.rendering.BasicConnectionStyle;
\r
48 import org.simantics.diagram.connection.rendering.ConnectionStyle;
\r
49 import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;
\r
50 import org.simantics.diagram.connection.rendering.arrows.ArrowLineEndStyle;
\r
51 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
\r
52 import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle;
\r
53 import org.simantics.diagram.content.EdgeResource;
\r
54 import org.simantics.diagram.content.ResourceTerminal;
\r
55 import org.simantics.diagram.content.TerminalMap;
\r
56 import org.simantics.diagram.query.DiagramRequests;
\r
57 import org.simantics.diagram.stubs.DiagramResource;
\r
58 import org.simantics.diagram.stubs.G2DResource;
\r
59 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
\r
60 import org.simantics.diagram.synchronization.graph.RouteGraphConnection;
\r
61 import org.simantics.diagram.ui.DiagramModelHints;
\r
62 import org.simantics.g2d.canvas.ICanvasContext;
\r
63 import org.simantics.g2d.connection.ConnectionEntity;
\r
64 import org.simantics.g2d.diagram.IDiagram;
\r
65 import org.simantics.g2d.diagram.handler.DataElementMap;
\r
66 import org.simantics.g2d.diagram.handler.Topology.Connection;
\r
67 import org.simantics.g2d.diagram.handler.Topology.Terminal;
\r
68 import org.simantics.g2d.element.ElementClass;
\r
69 import org.simantics.g2d.element.ElementHints;
\r
70 import org.simantics.g2d.element.ElementUtils;
\r
71 import org.simantics.g2d.element.IElement;
\r
72 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
\r
73 import org.simantics.g2d.element.handler.TerminalTopology;
\r
74 import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;
\r
75 import org.simantics.g2d.elementclass.FlagClass.Type;
\r
76 import org.simantics.g2d.utils.TopologicalSelectionExpander;
\r
77 import org.simantics.layer0.Layer0;
\r
78 import org.simantics.modeling.ModelingResources;
\r
79 import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;
\r
80 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;
\r
81 import org.simantics.scenegraph.utils.GeometryUtils;
\r
82 import org.simantics.structural.stubs.StructuralResource2;
\r
83 import org.simantics.structural2.modelingRules.CPTerminal;
\r
84 import org.simantics.structural2.modelingRules.IAttachmentRelationMap;
\r
85 import org.simantics.structural2.modelingRules.IModelingRules;
\r
88 * An element class for single connection entity elements. A connection entity
\r
89 * consists of connection edge segments and branch points as its children.
\r
91 * @author Tuukka Lehtonen
\r
93 public class RouteGraphConnectionClassFactory extends SyncElementFactory {
\r
95 private static final boolean DEBUG = false;
\r
97 public static final ElementClass CLASS = RouteGraphConnectionClass.CLASS;
\r
99 public static final ILineEndStyle HEAD = new ArrowLineEndStyle("fill 2 1 0");
\r
100 public static final ILineEndStyle TAIL = PlainLineEndStyle.INSTANCE;
\r
102 protected Layer0 L0;
\r
103 protected DiagramResource DIA;
\r
104 protected StructuralResource2 STR;
\r
105 protected ModelingResources MOD;
\r
107 public RouteGraphConnectionClassFactory(ReadGraph graph) {
\r
108 this.L0 = Layer0.getInstance(graph);
\r
109 this.DIA = DiagramResource.getInstance(graph);
\r
110 this.STR = StructuralResource2.getInstance(graph);
\r
111 this.MOD = ModelingResources.getInstance(graph);
\r
115 public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType,
\r
116 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 DIA.Connection;
\r
126 public void load(ReadGraph graph, ICanvasContext canvas, IDiagram diagram, final Resource connection,
\r
127 IElement element) throws DatabaseException {
\r
129 IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES);
\r
130 Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);
\r
132 RouteGraph rg = new RouteGraph();
\r
134 // Default capacity should be enough for common cases.
\r
135 Set<EdgeResource> links = new THashSet<EdgeResource>();
\r
136 Map<Object, RouteNode> nodeByData = new THashMap<Object, RouteNode>();
\r
138 // Load all route graph interior RouteNodes: route lines and points
\r
139 for (Resource interiorNode : graph.getObjects(connection, DIA.HasInteriorRouteNode)) {
\r
140 if (graph.isInstanceOf(interiorNode, DIA.RouteLine)) {
\r
141 Boolean isHorizontal = graph.getRelatedValue(interiorNode, DIA.IsHorizontal, Bindings.BOOLEAN);
\r
142 Double position = graph.getRelatedValue(interiorNode, DIA.HasPosition, Bindings.DOUBLE);
\r
143 RouteLine line = rg.addLine(isHorizontal, position);
\r
144 line.setData( RouteGraphConnection.serialize(graph, interiorNode) );
\r
146 nodeByData.put( interiorNode, line );
\r
148 for (Resource connectedTo : graph.getObjects(interiorNode, DIA.AreConnected)) {
\r
149 links.add( new EdgeResource(interiorNode, connectedTo) );
\r
151 } else if (graph.isInstanceOf(interiorNode, DIA.RoutePoint)) {
\r
152 // Not supported yet. Ignore.
\r
156 Rectangle2D bounds = new Rectangle2D.Double();
\r
157 Map<Resource, Resource> connectorToModeledAttachment = null;
\r
159 // Primarily the loader will believe what modeling rules say about
\r
160 // connector attachment relations.
\r
162 // If modeling rules decide nothing, then we simply believe what the
\r
163 // the attachment relations in the graph say.
\r
165 // Special case 1: connection with two (2) terminals
\r
166 // If the attachment of one of two terminals is decided by modeling
\r
167 // rules, the other attachment shall be the opposite of the decided
\r
168 // attachment (see forcedAttachmentRelation below).
\r
170 // Special case 2: connected to a flag
\r
171 // If the attached element is a flag and modeling rules say nothing
\r
172 // about it, believe the direction stated by the flag type.
\r
174 Collection<Statement> toConnectorStatements = graph.getStatements(connection, DIA.HasConnector);
\r
175 int terminalCount = 0;
\r
177 // See if modeling rules judge any of the connection terminal attachments.
\r
178 if (modelingRules != null) {
\r
179 for (Statement toConnector : toConnectorStatements) {
\r
180 Resource connector = toConnector.getObject();
\r
182 Statement terminalStm = findTerminalStatement(graph, connection, connector);
\r
183 if (terminalStm == null)
\r
184 // Ignore broken connector: attached to the connection but not to any terminal.
\r
187 Resource terminalElement = terminalStm.getObject();
\r
188 Resource connectionRelation = graph.getPossibleInverse(terminalStm.getPredicate());
\r
189 if (connectionRelation == null)
\r
194 IAttachmentRelationMap map = modelingRules.getAttachmentRelations(graph, connection);
\r
195 Resource attachment = map.get(graph, new CPTerminal(terminalElement, connectionRelation));
\r
196 if (attachment != null) {
\r
197 // Primary: believe modeling rules
\r
198 if (connectorToModeledAttachment == null)
\r
199 connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());
\r
200 connectorToModeledAttachment.put(connector, attachment);
\r
202 System.out.println("modeling rules decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
\r
203 } else if (graph.isInstanceOf(terminalElement, DIA.Flag)) {
\r
204 // Secondary: believe flag type
\r
205 attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules);
\r
206 if (attachment != null) {
\r
207 if (connectorToModeledAttachment == null)
\r
208 connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());
\r
209 connectorToModeledAttachment.put(connector, attachment);
\r
211 System.out.println("flag type decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
\r
217 if (connectorToModeledAttachment == null)
\r
218 connectorToModeledAttachment = Collections.emptyMap();
\r
220 Resource forcedAttachmentRelation = null;
\r
221 if (terminalCount == 2 && connectorToModeledAttachment.size() == 1) {
\r
222 forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next());
\r
224 System.out.println("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation));
\r
227 Resource connectionType = graph.getPossibleObject(connection, STR.HasConnectionType);
\r
229 // Needed to support ConnectionEntity#getTerminalConnections
\r
230 Set<BackendConnection> backendConnections = new THashSet<BackendConnection>(toConnectorStatements.size(), 0.75f);
\r
232 // Load all node terminal connections as RouteTerminals
\r
233 for (Statement toConnector : toConnectorStatements) {
\r
234 Resource connector = toConnector.getObject();
\r
235 Resource attachmentRelation = toConnector.getPredicate();
\r
237 System.out.println("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation));
\r
239 Statement terminalStm = findTerminalStatement(graph, connection, connector);
\r
240 if (terminalStm == null)
\r
241 // Ignore broken connector: attached to the connection but not to any terminal.
\r
244 Resource terminalElement = terminalStm.getObject();
\r
245 Resource terminalElementType = graph.getPossibleType(terminalElement, DIA.Element);
\r
246 if (terminalElementType == null)
\r
247 // Ignore non-element terminal elements
\r
250 Resource connectionRelation = graph.getPossibleInverse(terminalStm.getPredicate());
\r
251 if (connectionRelation == null)
\r
254 // Discover node and terminal this connector is connected to.
\r
255 TerminalMap terminals = graph.syncRequest(DiagramRequests.elementTypeTerminals(terminalElementType),
\r
256 TransientCacheListener.<TerminalMap>instance());
\r
257 Resource terminal = terminals.getTerminal(connectionRelation);
\r
258 if (terminal == null) {
\r
259 System.err.println(getClass().getSimpleName()
\r
260 + ": Could not find terminal for connection point "
\r
261 + NameUtils.getSafeName(graph, connectionRelation, true)
\r
263 + NameUtils.getSafeName(graph, terminalElement, true));
\r
267 double[] position = graph.getRelatedValue(connector, DIA.HasRelativeLocation, Bindings.DOUBLE_ARRAY);
\r
268 if (position.length != 2)
\r
269 position = new double[] { 0, 0 };
\r
272 System.out.println("terminalStm: " + NameUtils.toString(graph, terminalStm));
\r
273 System.out.println("terminal: " + graph.getURI(terminalStm.getPredicate()));
\r
275 AffineTransform terminalElementTr = DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement);
\r
277 System.out.println("terminalElementTr: " + terminalElementTr);
\r
279 double x = terminalElementTr.getTranslateX();
\r
280 double y = terminalElementTr.getTranslateY();
\r
281 double minx = x-1, miny = y-1, maxx = x+1, maxy = y+1;
\r
282 int direction = 0x0;
\r
284 // Use modelingRules to ascertain the proper attachmentRelation
\r
285 // for this terminal connection, if available.
\r
286 Resource att = connectorToModeledAttachment.get(connector);
\r
288 attachmentRelation = att;
\r
290 System.out.println("modeling rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
\r
291 } else if (forcedAttachmentRelation != null) {
\r
292 attachmentRelation = forcedAttachmentRelation;
\r
294 System.out.println("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
\r
297 System.out.println("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
\r
299 // Get element bounds to decide allowed terminal direction(s)
\r
300 IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));
\r
301 ElementUtils.getElementBounds(te, bounds);
\r
303 Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalElementTr);
\r
304 bounds.setFrame(shp.getBounds2D());
\r
307 // Expand bounds by 2mm to make the connections enter the terminals
\r
308 // at a straight angle and from a distance instead of coming in
\r
310 GeometryUtils.expandRectangle(bounds, 2);
\r
311 minx = bounds.getMinX();
\r
312 miny = bounds.getMinY();
\r
313 maxx = bounds.getMaxX();
\r
314 maxy = bounds.getMaxY();
\r
316 AffineTransform terminalPos = DiagramGraphUtil.getDynamicAffineTransform(graph, terminalElement, terminal);
\r
317 //AffineTransform terminalPos2 = DiagramGraphUtil.getAffineTransform(graph, terminal);
\r
318 if (terminalPos != null) {
\r
320 System.out.println("terminalPos: " + terminalPos);
\r
321 //System.out.println("terminalPos2: " + terminalPos2);
\r
323 terminalElementTr.concatenate(terminalPos);
\r
325 System.out.println("terminalElementTr: " + terminalElementTr);
\r
326 x = terminalElementTr.getTranslateX();
\r
327 y = terminalElementTr.getTranslateY();
\r
330 Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER);
\r
331 if (allowedDirections != null) {
\r
332 direction |= allowedDirections;
\r
333 direction = RouteGraphUtils.rotateDirection(direction, terminalElementTr);
\r
335 direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds);
\r
337 //System.out.println("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction));
\r
339 backendConnections.add(
\r
340 new BackendConnection(
\r
341 toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin),
\r
346 if (direction == 0)
\r
347 // Accept any horizontal/vertical direction if nothing is defined
\r
350 if (graph.<Boolean>getRelatedValue(connector, DIA.Connector_straight, Bindings.BOOLEAN))
\r
351 direction |= RouteTerminal.DIR_DIRECT;
\r
352 // FIXME: routegraph crashes if this is done for all terminals regardless of the amount of terminals.
\r
355 System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));
\r
356 ILineEndStyle endStyle = loadLineEndStyle(graph, attachmentRelation, connectionType, TAIL);
\r
358 RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle);
\r
359 routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) );
\r
361 nodeByData.put( connector, routeTerminal );
\r
363 for (Resource connectedTo : graph.getObjects(connector, DIA.AreConnected)) {
\r
364 links.add( new EdgeResource(connectedTo, connector) );
\r
368 // Finish route graph loading by Linking route nodes together
\r
369 for (EdgeResource link : links) {
\r
370 RouteNode n1 = nodeByData.get(link.first());
\r
371 RouteNode n2 = nodeByData.get(link.second());
\r
372 if (n1 == null || n2 == null) {
\r
373 System.err.println("Stray connection link found: " + link.toString(graph));
\r
379 // Load connection line style.
\r
380 ConnectionStyle style = readConnectionStyle(graph, modelingRules, connection);
\r
381 StyledRouteGraphRenderer renderer = getRenderer(graph, style);
\r
383 // Finish element load
\r
384 element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg);
\r
385 element.setHint(RouteGraphConnectionClass.KEY_RENDERER, renderer);
\r
386 element.setHint(RouteGraphConnectionClass.KEY_PICK_TOLERANCE, 0.5);
\r
388 // Initialize ConnectionEntity in element
\r
389 element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(diagram, connection, element, backendConnections));
\r
391 // Setup graph writeback support for route graph modifications
\r
392 final Session session = graph.getSession();
\r
393 element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() {
\r
395 public void routeGraphChanged(RouteGraphChangeEvent event) {
\r
396 scheduleSynchronize(session, connection, event);
\r
401 protected EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue)
\r
402 throws DatabaseException {
\r
403 if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))
\r
404 return EdgeEnd.Begin;
\r
405 if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))
\r
406 return EdgeEnd.End;
\r
407 return defaultValue;
\r
415 * @throws DatabaseException
\r
417 protected StyledRouteGraphRenderer getRenderer(ReadGraph graph, ConnectionStyle style)
\r
418 throws DatabaseException {
\r
419 return graph.syncRequest(new Renderer(style),
\r
420 TransientCacheListener.<StyledRouteGraphRenderer>instance());
\r
424 * A request for caching StyledRouteGraphRenderer results.
\r
426 public static class Renderer extends UnaryRead<ConnectionStyle, StyledRouteGraphRenderer> {
\r
427 public Renderer(ConnectionStyle style) {
\r
431 public StyledRouteGraphRenderer perform(ReadGraph graph) throws DatabaseException {
\r
432 return new StyledRouteGraphRenderer(parameter);
\r
439 * @param modelingRules
\r
440 * @param connection
\r
442 * @throws DatabaseException
\r
444 protected ConnectionStyle readConnectionStyle(ReadGraph graph, IModelingRules modelingRules, Resource connection) throws DatabaseException {
\r
445 Resource connectionType = null;
\r
446 if (modelingRules != null)
\r
447 connectionType = modelingRules.getConnectionType(graph, connection);
\r
448 if (connectionType == null)
\r
449 connectionType = graph.getPossibleObject(connection, STR.HasConnectionType);
\r
450 return readConnectionStyleFromConnectionType(graph, connectionType);
\r
453 protected ConnectionStyle readConnectionStyleFromConnectionType(ReadGraph graph, Resource connectionType) throws DatabaseException {
\r
454 return graph.syncRequest(new ReadConnectionStyleFromConnectionType(connectionType),
\r
455 TransientCacheListener.<ConnectionStyle>instance());
\r
459 * A request for caching ConnectionStyle results.
\r
461 public static class ReadConnectionStyleFromConnectionType extends UnaryRead<Resource, ConnectionStyle> {
\r
462 public ReadConnectionStyleFromConnectionType(Resource connectionType) {
\r
463 super(connectionType);
\r
466 public ConnectionStyle perform(ReadGraph graph) throws DatabaseException {
\r
467 return readConnectionStyleFromConnectionType0(graph, parameter);
\r
471 protected static ConnectionStyle readConnectionStyleFromConnectionType0(ReadGraph graph, Resource connectionType) throws DatabaseException {
\r
472 ConnectionVisuals cv = null;
\r
473 if (connectionType != null)
\r
474 cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType),
\r
475 TransientCacheListener.<ConnectionVisuals>instance());
\r
477 // Fixed style settings
\r
478 Color branchPointColor = Color.BLACK;
\r
479 double branchPointRadius = 0.5;
\r
480 double degenerateLineLength = 0.8;
\r
482 Color lineColor = cv != null ? cv.toColor() : null;
\r
483 if (lineColor == null)
\r
484 lineColor = Color.DARK_GRAY;
\r
485 Stroke lineStroke = cv != null ? cv.stroke : null;
\r
486 if (lineStroke == null)
\r
487 lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0);
\r
488 Stroke routeLineStroke = GeometryUtils.scaleStrokeWidth(lineStroke, 2);
\r
490 return new BasicConnectionStyle(
\r
496 degenerateLineLength);
\r
501 * @param connection
\r
503 * @return connection relation statement from diagram connection connector
\r
505 * @throws DatabaseException
\r
507 public Statement findTerminalStatement(ReadGraph graph, Resource connection, Resource connector)
\r
508 throws DatabaseException {
\r
509 for (Statement stm : graph.getStatements(connector, STR.Connects)) {
\r
510 if (connection.equals(stm.getObject()))
\r
517 public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, ILineEndStyle defaultValue)
\r
518 throws DatabaseException {
\r
519 ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation),
\r
520 TransientCacheListener.<ILineEndStyle>instance());
\r
521 return style != null ? style : defaultValue;
\r
524 public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, Resource connectionType, ILineEndStyle defaultValue)
\r
525 throws DatabaseException {
\r
526 if(connectionType != null) {
\r
527 ILineEndStyle style = graph.syncRequest(new LineEndStyleWithType(attachmentRelation, connectionType),
\r
528 TransientCacheListener.<ILineEndStyle>instance());
\r
529 return style != null ? style : defaultValue;
\r
531 ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation),
\r
532 TransientCacheListener.<ILineEndStyle>instance());
\r
533 return style != null ? style : defaultValue;
\r
538 * A request for caching ILineEndStyle results.
\r
540 public static class LineEndStyle extends UnaryRead<Resource, ILineEndStyle> {
\r
541 public LineEndStyle(Resource attachmentRelation) {
\r
542 super(attachmentRelation);
\r
545 public ILineEndStyle perform(ReadGraph graph) throws DatabaseException {
\r
546 return loadLineEndStyle0(graph, parameter);
\r
550 public static class LineEndStyleWithType extends ResourceRead2<ILineEndStyle> {
\r
551 public LineEndStyleWithType(Resource attachmentRelation, Resource connectionType) {
\r
552 super(attachmentRelation, connectionType);
\r
555 public ILineEndStyle perform(ReadGraph graph) throws DatabaseException {
\r
556 return loadLineEndStyle0(graph, resource, resource2);
\r
560 public static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation)
\r
561 throws DatabaseException {
\r
562 ILineEndStyle style = graph.getPossibleAdapter(attachmentRelation, ILineEndStyle.class);
\r
565 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
566 if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))
\r
568 if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))
\r
573 public static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation, Resource connectionType)
\r
574 throws DatabaseException {
\r
575 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
576 if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) {
\r
577 if(connectionType != null) {
\r
578 G2DResource G2D = G2DResource.getInstance(graph);
\r
579 Resource end = graph.getPossibleObject(connectionType, G2D.HasEndArrow);
\r
582 Double size = graph.getPossibleRelatedValue(end, G2D.HasSize, Bindings.DOUBLE);
\r
583 if(size == null) size = 0.0;
\r
584 Double widthRatio = graph.getPossibleRelatedValue(end, G2D.HasWidthRatio, Bindings.DOUBLE);
\r
585 if(widthRatio == null) widthRatio = 1.0;
\r
586 Double space = graph.getPossibleRelatedValue(end, G2D.HasSpace, Bindings.DOUBLE);
\r
587 if(space == null) space = 0.0;
\r
589 Resource c = graph.getPossibleObject(end, G2D.HasColor);
\r
590 Color color = null;
\r
592 float[] col = graph.getPossibleValue(c, Bindings.FLOAT_ARRAY);
\r
593 if (col != null && col.length >= 3) {
\r
594 color = new Color(col[0], col[1], col[2]);
\r
598 return new ArrowLineEndStyle(size, widthRatio*size, space, color);
\r
603 return loadLineEndStyle0(graph, attachmentRelation);
\r
608 * @param attachmentRelation
\r
610 * @throws DatabaseException
\r
612 public Resource getInverseAttachment(ReadGraph graph, Resource attachmentRelation)
\r
613 throws DatabaseException {
\r
614 Resource inverse = graph.getPossibleObject(attachmentRelation, DIA.HasInverseAttachment);
\r
615 if (inverse != null)
\r
617 if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector))
\r
618 return DIA.HasPlainConnector;
\r
619 if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector))
\r
620 return DIA.HasArrowConnector;
\r
624 // private int directionSetToToDirectionMask(DirectionSet directions) {
\r
625 // if (directions == DirectionSet.ANY)
\r
628 // for (Double d : directions) {
\r
629 // mask |= compassAngleToToDirectionMask(d);
\r
634 // private int compassAngleToToDirectionMask(double compassAngle) {
\r
635 // Double d = DirectionSet.NESW.getClosestDirection(compassAngle);
\r
636 // int id = (int) Math.round(d);
\r
638 // // 0 right (1,0)
\r
640 // // 2 left (-1,0)
\r
643 // case 0: return (1 << 3);
\r
644 // case 90: return (1 << 2);
\r
645 // case 180: return (1 << 1);
\r
646 // case 270: return (1 << 0);
\r
651 public Resource resolveFlagAttachment(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException {
\r
652 Type type = resolveFlagType(graph, connection, flag, modelingRules);
\r
653 if (type != null) {
\r
655 case In: return DIA.HasPlainConnector;
\r
656 case Out: return DIA.HasArrowConnector;
\r
662 protected Type resolveFlagType(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException {
\r
663 return readFlagType(graph, flag);
\r
664 // StructuralResource2 sr = StructuralResource2.getInstance(graph);
\r
665 // Resource connectionType = graph.getPossibleObject(connection, sr.HasConnectionType);
\r
666 // if (connectionType == null)
\r
667 // return readFlagType(graph, flagElement, flag);
\r
668 // IFlagTypeReader ftr = graph.getPossibleAdapter(connectionType, IFlagTypeReader.class);
\r
669 // if (ftr == null)
\r
670 // return readFlagType(graph, flagElement, flag);
\r
672 // IFlagType ft = ftr.read(graph, flag, modelingRules);
\r
673 // FlagInfo info = ft.getInfo(graph);
\r
674 // return info.getType();
\r
677 protected Type readFlagType(ReadGraph graph, Resource flag) throws DatabaseException {
\r
678 Resource flagType = graph.getPossibleObject(flag, DIA.HasFlagType);
\r
679 Type type = DiagramGraphUtil.toFlagType(DIA, flagType);
\r
683 public static void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) {
\r
684 session.asyncRequest(RouteGraphConnection.synchronizer(connection, event));
\r
688 * Must have this in order for {@link TopologicalSelectionExpander} to work.
\r
689 * Otherwise this is pretty useless and should be deprecated altogether.
\r
691 * @see ElementHints#KEY_CONNECTION_ENTITY
\r
693 public static class CE implements ConnectionEntity {
\r
696 * Needed to gain access to DataElementMap.
\r
698 final IDiagram diagram;
\r
699 final DataElementMap dataMap;
\r
702 * The connection instance resource in the graph backend.
\r
704 final Resource connection;
\r
707 * The current element mapped to connection.
\r
709 IElement connectionElement;
\r
712 * @see #getTerminalConnections(Collection)
\r
714 final Set<BackendConnection> backendConnections;
\r
719 Set<Connection> terminalConnections;
\r
721 public CE(IDiagram diagram, Resource connection, IElement connectionElement, Set<BackendConnection> backendConnections) {
\r
722 if (connectionElement == null)
\r
723 throw new NullPointerException("null connection element");
\r
724 this.diagram = diagram;
\r
725 this.dataMap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
726 this.connection = connection;
\r
727 this.connectionElement = connectionElement;
\r
728 this.backendConnections = backendConnections;
\r
729 IElement ce = getConnection0();
\r
731 this.connectionElement = ce;
\r
734 public IElement getConnection0() {
\r
735 DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
736 IElement connectionElement = dem.getElement(diagram, connection);
\r
737 return connectionElement;
\r
741 public IElement getConnection() {
\r
742 IElement c = getConnection0();
\r
744 c = this.connectionElement;
\r
749 public Collection<IElement> getBranchPoints(Collection<IElement> result) {
\r
750 return result != null ? result : Collections.<IElement> emptyList();
\r
754 public Collection<IElement> getSegments(Collection<IElement> result) {
\r
755 return result != null ? result : Collections.<IElement> emptyList();
\r
759 public Collection<Connection> getTerminalConnections(Collection<Connection> result) {
\r
760 if (terminalConnections == null)
\r
761 terminalConnections = calculateTerminalConnections();
\r
762 if (result == null)
\r
763 result = new ArrayList<Connection>(terminalConnections);
\r
765 result.addAll(terminalConnections);
\r
766 return terminalConnections;
\r
769 private Set<Connection> calculateTerminalConnections() {
\r
770 Set<Connection> result = new THashSet<Connection>(backendConnections.size());
\r
771 DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
772 IElement connectionElement = dem.getElement(diagram, connection);
\r
773 if (connectionElement == null)
\r
774 throw new NullPointerException("connection is not mapped");
\r
775 ArrayList<Terminal> ts = new ArrayList<Terminal>();
\r
776 for (BackendConnection bc : backendConnections) {
\r
777 IElement e = dem.getElement(diagram, bc.node);
\r
780 TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class);
\r
782 tt.getTerminals(e, ts);
\r
783 for (Terminal t : ts) {
\r
784 if (t instanceof ResourceTerminal) {
\r
785 ResourceTerminal rt = (ResourceTerminal) t;
\r
786 if (bc.terminal.equals(rt.getResource())) {
\r
787 result.add(new Connection(connectionElement, bc.end, e, t));
\r
797 public void setListener(ConnectionListener listener) {
\r
798 throw new UnsupportedOperationException();
\r
802 public String toString() {
\r
803 return getClass().getSimpleName() + "[resource=" + connection + ", connectionElement=" + getConnection()
\r
809 public static class BackendConnection {
\r
810 public final Resource node;
\r
811 public final Resource terminal;
\r
812 public final EdgeEnd end;
\r
813 public BackendConnection(EdgeEnd end, Resource node, Resource terminal) {
\r
814 assert end != null;
\r
815 assert node != null;
\r
816 assert terminal != null;
\r
819 this.terminal = terminal;
\r
822 public boolean equals(Object obj) {
\r
825 if (!(obj instanceof Connection))
\r
827 Connection other = (Connection) obj;
\r
828 return other.terminal == terminal
\r
829 && other.node == node
\r
830 && other.end == end;
\r
833 public int hashCode() {
\r
834 final int prime = 31;
\r
836 result = prime * result + end.hashCode();
\r
837 result = prime * result + ((node == null) ? 0 : node.hashCode());
\r
838 result = prime * result + ((terminal == null) ? 0 : terminal.hashCode());
\r
842 public String toString() {
\r
843 return "BackendConnection[node=" + node + ", terminal=" + terminal + ", end=" + end + "]";
\r