/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.diagram.adapter; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.Statement; import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.request.ResourceRead2; import org.simantics.db.common.request.UnaryRead; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.AsyncProcedure; import org.simantics.diagram.connection.ConnectionVisuals; import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.connection.RouteGraphConnectionClass; import org.simantics.diagram.connection.RouteLine; import org.simantics.diagram.connection.RouteNode; import org.simantics.diagram.connection.RouteTerminal; import org.simantics.diagram.connection.rendering.BasicConnectionStyle; import org.simantics.diagram.connection.rendering.ConnectionStyle; import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer; import org.simantics.diagram.connection.rendering.arrows.ArrowLineEndStyle; import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle; import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle; import org.simantics.diagram.content.EdgeResource; import org.simantics.diagram.content.ResourceTerminal; import org.simantics.diagram.content.TerminalMap; import org.simantics.diagram.query.DiagramRequests; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.stubs.G2DResource; import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.diagram.synchronization.graph.RouteGraphConnection; import org.simantics.diagram.ui.DiagramModelHints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.connection.ConnectionEntity; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.DataElementMap; import org.simantics.g2d.diagram.handler.Topology.Connection; import org.simantics.g2d.diagram.handler.Topology.Terminal; import org.simantics.g2d.element.ElementClass; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; import org.simantics.g2d.element.handler.TerminalTopology; import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; import org.simantics.g2d.elementclass.FlagClass.Type; import org.simantics.g2d.utils.TopologicalSelectionExpander; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener; import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent; import org.simantics.scenegraph.utils.GeometryUtils; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.modelingRules.CPTerminal; import org.simantics.structural2.modelingRules.IAttachmentRelationMap; import org.simantics.structural2.modelingRules.IModelingRules; /** * An element class for single connection entity elements. A connection entity * consists of connection edge segments and branch points as its children. * * @author Tuukka Lehtonen */ public class RouteGraphConnectionClassFactory extends SyncElementFactory { private static final boolean DEBUG = false; public static final ElementClass CLASS = RouteGraphConnectionClass.CLASS; public static final ILineEndStyle HEAD = new ArrowLineEndStyle("fill 2 1 0"); public static final ILineEndStyle TAIL = PlainLineEndStyle.INSTANCE; protected Layer0 L0; protected DiagramResource DIA; protected StructuralResource2 STR; protected ModelingResources MOD; public RouteGraphConnectionClassFactory(ReadGraph graph) { this.L0 = Layer0.getInstance(graph); this.DIA = DiagramResource.getInstance(graph); this.STR = StructuralResource2.getInstance(graph); this.MOD = ModelingResources.getInstance(graph); } @Override public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, final AsyncProcedure procedure) { procedure.execute(graph, CLASS.newClassWith(false, new StaticObjectAdapter(elementType))); } @Override protected Resource getElementClassBaseType(AsyncReadGraph graph) { return DIA.Connection; } @Override public void load(ReadGraph graph, ICanvasContext canvas, IDiagram diagram, final Resource connection, IElement element) throws DatabaseException { IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES); Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE); RouteGraph rg = new RouteGraph(); // Default capacity should be enough for common cases. Set links = new THashSet(); Map nodeByData = new THashMap(); // Load all route graph interior RouteNodes: route lines and points for (Resource interiorNode : graph.getObjects(connection, DIA.HasInteriorRouteNode)) { if (graph.isInstanceOf(interiorNode, DIA.RouteLine)) { Boolean isHorizontal = graph.getRelatedValue(interiorNode, DIA.IsHorizontal, Bindings.BOOLEAN); Double position = graph.getRelatedValue(interiorNode, DIA.HasPosition, Bindings.DOUBLE); RouteLine line = rg.addLine(isHorizontal, position); line.setData( RouteGraphConnection.serialize(graph, interiorNode) ); nodeByData.put( interiorNode, line ); for (Resource connectedTo : graph.getObjects(interiorNode, DIA.AreConnected)) { links.add( new EdgeResource(interiorNode, connectedTo) ); } } else if (graph.isInstanceOf(interiorNode, DIA.RoutePoint)) { // Not supported yet. Ignore. } } Rectangle2D bounds = new Rectangle2D.Double(); Map connectorToModeledAttachment = null; // Primarily the loader will believe what modeling rules say about // connector attachment relations. // // If modeling rules decide nothing, then we simply believe what the // the attachment relations in the graph say. // // Special case 1: connection with two (2) terminals // If the attachment of one of two terminals is decided by modeling // rules, the other attachment shall be the opposite of the decided // attachment (see forcedAttachmentRelation below). // // Special case 2: connected to a flag // If the attached element is a flag and modeling rules say nothing // about it, believe the direction stated by the flag type. Collection toConnectorStatements = graph.getStatements(connection, DIA.HasConnector); int terminalCount = 0; // See if modeling rules judge any of the connection terminal attachments. if (modelingRules != null) { for (Statement toConnector : toConnectorStatements) { Resource connector = toConnector.getObject(); Statement terminalStm = findTerminalStatement(graph, connection, connector); if (terminalStm == null) // Ignore broken connector: attached to the connection but not to any terminal. continue; Resource terminalElement = terminalStm.getObject(); Resource connectionRelation = graph.getPossibleInverse(terminalStm.getPredicate()); if (connectionRelation == null) continue; ++terminalCount; IAttachmentRelationMap map = modelingRules.getAttachmentRelations(graph, connection); Resource attachment = map.get(graph, new CPTerminal(terminalElement, connectionRelation)); if (attachment != null) { // Primary: believe modeling rules if (connectorToModeledAttachment == null) connectorToModeledAttachment = new THashMap(toConnectorStatements.size()); connectorToModeledAttachment.put(connector, attachment); if (DEBUG) System.out.println("modeling rules decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")"); } else if (graph.isInstanceOf(terminalElement, DIA.Flag)) { // Secondary: believe flag type attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules); if (attachment != null) { if (connectorToModeledAttachment == null) connectorToModeledAttachment = new THashMap(toConnectorStatements.size()); connectorToModeledAttachment.put(connector, attachment); if (DEBUG) System.out.println("flag type decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")"); } } } } if (connectorToModeledAttachment == null) connectorToModeledAttachment = Collections.emptyMap(); Resource forcedAttachmentRelation = null; if (terminalCount == 2 && connectorToModeledAttachment.size() == 1) { forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next()); if (DEBUG) System.out.println("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation)); } Resource connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); // Needed to support ConnectionEntity#getTerminalConnections Set backendConnections = new THashSet(toConnectorStatements.size(), 0.75f); // Load all node terminal connections as RouteTerminals for (Statement toConnector : toConnectorStatements) { Resource connector = toConnector.getObject(); Resource attachmentRelation = toConnector.getPredicate(); if (DEBUG) System.out.println("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation)); Statement terminalStm = findTerminalStatement(graph, connection, connector); if (terminalStm == null) // Ignore broken connector: attached to the connection but not to any terminal. continue; Resource terminalElement = terminalStm.getObject(); Resource terminalElementType = graph.getPossibleType(terminalElement, DIA.Element); if (terminalElementType == null) // Ignore non-element terminal elements continue; Resource connectionRelation = graph.getPossibleInverse(terminalStm.getPredicate()); if (connectionRelation == null) continue; // Discover node and terminal this connector is connected to. TerminalMap terminals = graph.syncRequest(DiagramRequests.elementTypeTerminals(terminalElementType), TransientCacheListener.instance()); Resource terminal = terminals.getTerminal(connectionRelation); if (terminal == null) { System.err.println(getClass().getSimpleName() + ": Could not find terminal for connection point " + NameUtils.getSafeName(graph, connectionRelation, true) + " in element " + NameUtils.getSafeName(graph, terminalElement, true)); continue; } double[] position = graph.getRelatedValue(connector, DIA.HasRelativeLocation, Bindings.DOUBLE_ARRAY); if (position.length != 2) position = new double[] { 0, 0 }; if (DEBUG) { System.out.println("terminalStm: " + NameUtils.toString(graph, terminalStm)); System.out.println("terminal: " + graph.getURI(terminalStm.getPredicate())); } AffineTransform terminalElementTr = DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement); if (DEBUG) System.out.println("terminalElementTr: " + terminalElementTr); double x = terminalElementTr.getTranslateX(); double y = terminalElementTr.getTranslateY(); double minx = x-1, miny = y-1, maxx = x+1, maxy = y+1; int direction = 0x0; // Use modelingRules to ascertain the proper attachmentRelation // for this terminal connection, if available. Resource att = connectorToModeledAttachment.get(connector); if (att != null) { attachmentRelation = att; if (DEBUG) System.out.println("modeling rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation)); } else if (forcedAttachmentRelation != null) { attachmentRelation = forcedAttachmentRelation; if (DEBUG) System.out.println("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation)); } if (DEBUG) System.out.println("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation)); // Get element bounds to decide allowed terminal direction(s) IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null)); ElementUtils.getElementBounds(te, bounds); { Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalElementTr); bounds.setFrame(shp.getBounds2D()); } // Expand bounds by 2mm to make the connections enter the terminals // at a straight angle and from a distance instead of coming in // "horizontally". GeometryUtils.expandRectangle(bounds, 2); minx = bounds.getMinX(); miny = bounds.getMinY(); maxx = bounds.getMaxX(); maxy = bounds.getMaxY(); AffineTransform terminalPos = DiagramGraphUtil.getDynamicAffineTransform(graph, terminalElement, terminal); //AffineTransform terminalPos2 = DiagramGraphUtil.getAffineTransform(graph, terminal); if (terminalPos != null) { if (DEBUG) { System.out.println("terminalPos: " + terminalPos); //System.out.println("terminalPos2: " + terminalPos2); } terminalElementTr.concatenate(terminalPos); if (DEBUG) System.out.println("terminalElementTr: " + terminalElementTr); x = terminalElementTr.getTranslateX(); y = terminalElementTr.getTranslateY(); } Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER); if (allowedDirections != null) { direction |= allowedDirections; direction = RouteGraphUtils.rotateDirection(direction, terminalElementTr); } else { direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds); } //System.out.println("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction)); backendConnections.add( new BackendConnection( toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin), terminalElement, terminal) ); if (direction == 0) // Accept any horizontal/vertical direction if nothing is defined direction = 0xf; if (graph.getRelatedValue(connector, DIA.Connector_straight, Bindings.BOOLEAN)) direction |= RouteTerminal.DIR_DIRECT; // FIXME: routegraph crashes if this is done for all terminals regardless of the amount of terminals. if (DEBUG) System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation)); ILineEndStyle endStyle = loadLineEndStyle(graph, attachmentRelation, connectionType, TAIL); RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle); routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) ); nodeByData.put( connector, routeTerminal ); for (Resource connectedTo : graph.getObjects(connector, DIA.AreConnected)) { links.add( new EdgeResource(connectedTo, connector) ); } } // Finish route graph loading by Linking route nodes together for (EdgeResource link : links) { RouteNode n1 = nodeByData.get(link.first()); RouteNode n2 = nodeByData.get(link.second()); if (n1 == null || n2 == null) { System.err.println("Stray connection link found: " + link.toString(graph)); continue; } rg.link(n1, n2); } // Load connection line style. ConnectionStyle style = readConnectionStyle(graph, modelingRules, connection); StyledRouteGraphRenderer renderer = getRenderer(graph, style); // Finish element load element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg); element.setHint(RouteGraphConnectionClass.KEY_RENDERER, renderer); element.setHint(RouteGraphConnectionClass.KEY_PICK_TOLERANCE, 0.5); // Initialize ConnectionEntity in element element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(diagram, connection, element, backendConnections)); // Setup graph writeback support for route graph modifications final Session session = graph.getSession(); element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() { @Override public void routeGraphChanged(RouteGraphChangeEvent event) { scheduleSynchronize(session, connection, event); } }); } protected EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue) throws DatabaseException { if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) return EdgeEnd.Begin; if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) return EdgeEnd.End; return defaultValue; } /** * @param graph * @param canvas * @param style * @return * @throws DatabaseException */ protected StyledRouteGraphRenderer getRenderer(ReadGraph graph, ConnectionStyle style) throws DatabaseException { return graph.syncRequest(new Renderer(style), TransientCacheListener.instance()); } /** * A request for caching StyledRouteGraphRenderer results. */ public static class Renderer extends UnaryRead { public Renderer(ConnectionStyle style) { super(style); } @Override public StyledRouteGraphRenderer perform(ReadGraph graph) throws DatabaseException { return new StyledRouteGraphRenderer(parameter); } } /** * @param graph * @param canvas * @param modelingRules * @param connection * @return * @throws DatabaseException */ protected ConnectionStyle readConnectionStyle(ReadGraph graph, IModelingRules modelingRules, Resource connection) throws DatabaseException { Resource connectionType = null; if (modelingRules != null) connectionType = modelingRules.getConnectionType(graph, connection); if (connectionType == null) connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); return readConnectionStyleFromConnectionType(graph, connectionType); } protected ConnectionStyle readConnectionStyleFromConnectionType(ReadGraph graph, Resource connectionType) throws DatabaseException { return graph.syncRequest(new ReadConnectionStyleFromConnectionType(connectionType), TransientCacheListener.instance()); } /** * A request for caching ConnectionStyle results. */ public static class ReadConnectionStyleFromConnectionType extends UnaryRead { public ReadConnectionStyleFromConnectionType(Resource connectionType) { super(connectionType); } @Override public ConnectionStyle perform(ReadGraph graph) throws DatabaseException { return readConnectionStyleFromConnectionType0(graph, parameter); } } protected static ConnectionStyle readConnectionStyleFromConnectionType0(ReadGraph graph, Resource connectionType) throws DatabaseException { ConnectionVisuals cv = null; if (connectionType != null) cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType), TransientCacheListener.instance()); // Fixed style settings Color branchPointColor = Color.BLACK; double branchPointRadius = 0.5; double degenerateLineLength = 0.8; Color lineColor = cv != null ? cv.toColor() : null; if (lineColor == null) lineColor = Color.DARK_GRAY; Stroke lineStroke = cv != null ? cv.stroke : null; if (lineStroke == null) lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0); Stroke routeLineStroke = GeometryUtils.scaleStrokeWidth(lineStroke, 2); return new BasicConnectionStyle( lineColor, branchPointColor, branchPointRadius, lineStroke, routeLineStroke, degenerateLineLength); } /** * @param graph * @param connection * @param connector * @return connection relation statement from diagram connection connector * to a node * @throws DatabaseException */ public Statement findTerminalStatement(ReadGraph graph, Resource connection, Resource connector) throws DatabaseException { for (Statement stm : graph.getStatements(connector, STR.Connects)) { if (connection.equals(stm.getObject())) continue; return stm; } return null; } public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, ILineEndStyle defaultValue) throws DatabaseException { ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation), TransientCacheListener.instance()); return style != null ? style : defaultValue; } public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, Resource connectionType, ILineEndStyle defaultValue) throws DatabaseException { if(connectionType != null) { ILineEndStyle style = graph.syncRequest(new LineEndStyleWithType(attachmentRelation, connectionType), TransientCacheListener.instance()); return style != null ? style : defaultValue; } else { ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation), TransientCacheListener.instance()); return style != null ? style : defaultValue; } } /** * A request for caching ILineEndStyle results. */ public static class LineEndStyle extends UnaryRead { public LineEndStyle(Resource attachmentRelation) { super(attachmentRelation); } @Override public ILineEndStyle perform(ReadGraph graph) throws DatabaseException { return loadLineEndStyle0(graph, parameter); } } public static class LineEndStyleWithType extends ResourceRead2 { public LineEndStyleWithType(Resource attachmentRelation, Resource connectionType) { super(attachmentRelation, connectionType); } @Override public ILineEndStyle perform(ReadGraph graph) throws DatabaseException { return loadLineEndStyle0(graph, resource, resource2); } } public static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation) throws DatabaseException { ILineEndStyle style = graph.getPossibleAdapter(attachmentRelation, ILineEndStyle.class); if (style != null) return style; DiagramResource DIA = DiagramResource.getInstance(graph); if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) return HEAD; if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) return TAIL; return null; } public static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation, Resource connectionType) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(graph); if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) { if(connectionType != null) { G2DResource G2D = G2DResource.getInstance(graph); Resource end = graph.getPossibleObject(connectionType, G2D.HasEndArrow); if(end != null) { Double size = graph.getPossibleRelatedValue(end, G2D.HasSize, Bindings.DOUBLE); if(size == null) size = 0.0; Double widthRatio = graph.getPossibleRelatedValue(end, G2D.HasWidthRatio, Bindings.DOUBLE); if(widthRatio == null) widthRatio = 1.0; Double space = graph.getPossibleRelatedValue(end, G2D.HasSpace, Bindings.DOUBLE); if(space == null) space = 0.0; Resource c = graph.getPossibleObject(end, G2D.HasColor); Color color = null; if (c != null) { float[] col = graph.getPossibleValue(c, Bindings.FLOAT_ARRAY); if (col != null && col.length >= 3) { color = new Color(col[0], col[1], col[2]); } } return new ArrowLineEndStyle(size, widthRatio*size, space, color); } } } return loadLineEndStyle0(graph, attachmentRelation); } /** * @param graph * @param attachmentRelation * @return * @throws DatabaseException */ public Resource getInverseAttachment(ReadGraph graph, Resource attachmentRelation) throws DatabaseException { Resource inverse = graph.getPossibleObject(attachmentRelation, DIA.HasInverseAttachment); if (inverse != null) return inverse; if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) return DIA.HasPlainConnector; if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) return DIA.HasArrowConnector; return null; } // private int directionSetToToDirectionMask(DirectionSet directions) { // if (directions == DirectionSet.ANY) // return 0xf; // int mask = 0; // for (Double d : directions) { // mask |= compassAngleToToDirectionMask(d); // } // return mask; // } // // private int compassAngleToToDirectionMask(double compassAngle) { // Double d = DirectionSet.NESW.getClosestDirection(compassAngle); // int id = (int) Math.round(d); // // bits: // // 0 right (1,0) // // 1 down (0,1) // // 2 left (-1,0) // // 3 up (0,-1) // switch (id) { // case 0: return (1 << 3); // case 90: return (1 << 2); // case 180: return (1 << 1); // case 270: return (1 << 0); // } // return 0xf; // } public Resource resolveFlagAttachment(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException { Type type = resolveFlagType(graph, connection, flag, modelingRules); if (type != null) { switch (type) { case In: return DIA.HasPlainConnector; case Out: return DIA.HasArrowConnector; } } return null; } protected Type resolveFlagType(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException { return readFlagType(graph, flag); // StructuralResource2 sr = StructuralResource2.getInstance(graph); // Resource connectionType = graph.getPossibleObject(connection, sr.HasConnectionType); // if (connectionType == null) // return readFlagType(graph, flagElement, flag); // IFlagTypeReader ftr = graph.getPossibleAdapter(connectionType, IFlagTypeReader.class); // if (ftr == null) // return readFlagType(graph, flagElement, flag); // // IFlagType ft = ftr.read(graph, flag, modelingRules); // FlagInfo info = ft.getInfo(graph); // return info.getType(); } protected Type readFlagType(ReadGraph graph, Resource flag) throws DatabaseException { Resource flagType = graph.getPossibleObject(flag, DIA.HasFlagType); Type type = DiagramGraphUtil.toFlagType(DIA, flagType); return type; } public static void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) { session.asyncRequest(RouteGraphConnection.synchronizer(connection, event)); } /** * Must have this in order for {@link TopologicalSelectionExpander} to work. * Otherwise this is pretty useless and should be deprecated altogether. * * @see ElementHints#KEY_CONNECTION_ENTITY */ public static class CE implements ConnectionEntity { /** * Needed to gain access to DataElementMap. */ final IDiagram diagram; final DataElementMap dataMap; /** * The connection instance resource in the graph backend. */ final Resource connection; /** * The current element mapped to connection. */ IElement connectionElement; /** * @see #getTerminalConnections(Collection) */ final Set backendConnections; /** * Cache. */ Set terminalConnections; public CE(IDiagram diagram, Resource connection, IElement connectionElement, Set backendConnections) { if (connectionElement == null) throw new NullPointerException("null connection element"); this.diagram = diagram; this.dataMap = diagram.getDiagramClass().getSingleItem(DataElementMap.class); this.connection = connection; this.connectionElement = connectionElement; this.backendConnections = backendConnections; IElement ce = getConnection0(); if (ce != null) this.connectionElement = ce; } public IElement getConnection0() { DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class); IElement connectionElement = dem.getElement(diagram, connection); return connectionElement; } @Override public IElement getConnection() { IElement c = getConnection0(); if (c == null) c = this.connectionElement; return c; } @Override public Collection getBranchPoints(Collection result) { return result != null ? result : Collections. emptyList(); } @Override public Collection getSegments(Collection result) { return result != null ? result : Collections. emptyList(); } @Override public Collection getTerminalConnections(Collection result) { if (terminalConnections == null) terminalConnections = calculateTerminalConnections(); if (result == null) result = new ArrayList(terminalConnections); else result.addAll(terminalConnections); return terminalConnections; } private Set calculateTerminalConnections() { Set result = new THashSet(backendConnections.size()); DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class); IElement connectionElement = dem.getElement(diagram, connection); if (connectionElement == null) throw new NullPointerException("connection is not mapped"); ArrayList ts = new ArrayList(); for (BackendConnection bc : backendConnections) { IElement e = dem.getElement(diagram, bc.node); if (e == null) continue; TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class); ts.clear(); tt.getTerminals(e, ts); for (Terminal t : ts) { if (t instanceof ResourceTerminal) { ResourceTerminal rt = (ResourceTerminal) t; if (bc.terminal.equals(rt.getResource())) { result.add(new Connection(connectionElement, bc.end, e, t)); break; } } } } return result; } @Override public void setListener(ConnectionListener listener) { throw new UnsupportedOperationException(); } @Override public String toString() { return getClass().getSimpleName() + "[resource=" + connection + ", connectionElement=" + getConnection() + "]"; } } public static class BackendConnection { public final Resource node; public final Resource terminal; public final EdgeEnd end; public BackendConnection(EdgeEnd end, Resource node, Resource terminal) { assert end != null; assert node != null; assert terminal != null; this.end = end; this.node = node; this.terminal = terminal; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Connection)) return false; Connection other = (Connection) obj; return other.terminal == terminal && other.node == node && other.end == end; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + end.hashCode(); result = prime * result + ((node == null) ? 0 : node.hashCode()); result = prime * result + ((terminal == null) ? 0 : terminal.hashCode()); return result; } @Override public String toString() { return "BackendConnection[node=" + node + ", terminal=" + terminal + ", end=" + end + "]"; } } }