/******************************************************************************* * Copyright (c) 2007, 2016 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 * Semantum Oy - refactoring *******************************************************************************/ package org.simantics.diagram.adapter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Set; import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.AsyncProcedure; import org.simantics.diagram.adapter.RouteGraphUtils.BackendConnection; import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.connection.RouteGraphConnectionClass; import org.simantics.diagram.connection.rendering.ConnectionStyle; import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer; import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle; import org.simantics.diagram.content.ResourceTerminal; import org.simantics.diagram.stubs.DiagramResource; 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.IElement; import org.simantics.g2d.element.handler.TerminalTopology; import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; 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.structural.stubs.StructuralResource2; import org.simantics.structural2.modelingRules.IModelingRules; import gnu.trove.set.hash.THashSet; /** * 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 { public static final ElementClass CLASS = RouteGraphConnectionClass.CLASS; public static final ILineEndStyle HEAD = RouteGraphUtils.HEAD; public static final ILineEndStyle TAIL = RouteGraphUtils.TAIL; 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); Set backendConnections = new THashSet<>(); RouteGraph rg = RouteGraphUtils.load(graph, diagramRuntime, connection, canvas, diagram, modelingRules, backendConnections); // Load connection line style. ConnectionStyle style = RouteGraphUtils.readConnectionStyle(graph, modelingRules, connection, STR); StyledRouteGraphRenderer renderer = RouteGraphUtils.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 Session session = graph.getSession(); element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() { @Override public void routeGraphChanged(RouteGraphChangeEvent event) { RouteGraphUtils.scheduleSynchronize(session, 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 */ private static class CE implements ConnectionEntity { /** * Needed to gain access to {@link DataElementMap}. */ final IDiagram diagram; /** * The connection instance resource in the graph database back-end. */ final Resource connection; /** * The current element mapped to connection. */ IElement connectionElement; /** * @see #getTerminalConnections(Collection) */ final Set backendConnections; /** * Cache. */ transient 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.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() + "]"; } } }