/******************************************************************************* | |
* <copyright> | |
* | |
* Copyright (c) 2013, 2013 SAP AG. | |
* 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: | |
* SAP AG - initial API, implementation and documentation | |
* | |
* </copyright> | |
* | |
*******************************************************************************/ | |
package org.eclipse.fmc.blockdiagram.editor.algorithm.connection; | |
import java.util.HashSet; | |
import java.util.Set; | |
import org.eclipse.fmc.blockdiagram.editor.algorithm.element.FMCElementAlgorithm; | |
import org.eclipse.fmc.blockdiagram.editor.features.add.CommunicationChannelAddFeature; | |
import org.eclipse.fmc.blockdiagram.editor.features.add.ConnectionAddFeature; | |
import org.eclipse.fmc.blockdiagram.editor.model.FMCTypeChecker; | |
import org.eclipse.fmc.blockdiagram.editor.model.FMCTypeCheckerFactory; | |
import org.eclipse.fmc.mm.DataflowDirection; | |
import org.eclipse.fmc.mm.RequestDirection; | |
import org.eclipse.graphiti.mm.algorithms.AbstractText; | |
import org.eclipse.graphiti.mm.algorithms.Ellipse; | |
import org.eclipse.graphiti.mm.algorithms.Polygon; | |
import org.eclipse.graphiti.mm.algorithms.Text; | |
import org.eclipse.graphiti.mm.algorithms.styles.Point; | |
import org.eclipse.graphiti.mm.pictograms.CompositeConnection; | |
import org.eclipse.graphiti.mm.pictograms.Connection; | |
import org.eclipse.graphiti.mm.pictograms.ConnectionDecorator; | |
import org.eclipse.graphiti.mm.pictograms.CurvedConnection; | |
import org.eclipse.graphiti.mm.pictograms.Diagram; | |
import org.eclipse.graphiti.services.Graphiti; | |
import org.eclipse.graphiti.services.IGaService; | |
import org.eclipse.graphiti.services.IPeService; | |
/** | |
* This class encapsulates the graphical connection algorithms for editing | |
* different types of connections such as communication channels or access | |
* connections. | |
* | |
* @author Benjamin Schmeling | |
* | |
*/ | |
public class FMCConnectionAlgorithm implements FMCElementAlgorithm { | |
private static final String R_DECO = "R"; | |
private static final float decoPosX = 0.4f; | |
private static final int[] POINTS_REQUEST_ARROW = new int[] { 0, 24, -15, | |
18, 0, 12, -3, 18 }; | |
private static final int[] POINTS_RESPONSE_ARROW = new int[] { 10, 24, 25, | |
18, 10, 12, 13, 18 }; | |
private FMCTypeChecker helper = FMCTypeCheckerFactory.getInstance(); | |
/** | |
* The minimum supported size of a channel line decoration (diameter). | |
*/ | |
public static final int CHANNEL_MIN_SIZE = 16; | |
/** | |
* Returns all connection decorators representing connection arrows (access | |
* connection) | |
* | |
* @param con | |
* The connection containing the decorators. | |
* @return Set of connection decorators. | |
*/ | |
private Set<ConnectionDecorator> getArrowDecorators(Connection con) { | |
Set<ConnectionDecorator> allArrows = new HashSet<ConnectionDecorator>(); | |
for (ConnectionDecorator deco : con.getConnectionDecorators()) { | |
if (deco.getGraphicsAlgorithm() instanceof Polygon) { | |
allArrows.add(deco); | |
} | |
} | |
return allArrows; | |
} | |
/** | |
* Returns the connection decorator representing the channel from the | |
* connection con. | |
* | |
* @param con | |
* The connection containing the channel decorator. | |
* @return The channel decorator, null if no such decorator exists. | |
* | |
*/ | |
private ConnectionDecorator getChannelDecorator(Connection con) { | |
for (ConnectionDecorator deco : con.getConnectionDecorators()) { | |
if (deco.getGraphicsAlgorithm() instanceof Ellipse) { | |
return deco; | |
} | |
} | |
return null; | |
} | |
/** | |
* Returns the connection decorator representing the R for Request. | |
* | |
* @param con | |
* The connection containing the R decorator. | |
* @return The R decorator, null if no such decorator exists. | |
* | |
*/ | |
private ConnectionDecorator getRequestRDecorator(Connection con) { | |
for (ConnectionDecorator deco : con.getConnectionDecorators()) { | |
if (deco.getGraphicsAlgorithm() instanceof AbstractText | |
&& R_DECO.equals(((AbstractText) deco | |
.getGraphicsAlgorithm()).getValue())) { | |
return deco; | |
} | |
} | |
return null; | |
} | |
/** | |
* Compares the polygon with the given points and returns true if both are | |
* equal. | |
* | |
* @param polyPoints | |
* The polygon points to compare. | |
* @param poly | |
* The polygon object to compare. | |
* @return True if both polygons are equal. | |
*/ | |
private boolean equalsPolygon(int[] polyPoints, Polygon poly) { | |
int i = 0; | |
for (Point p : poly.getPoints()) { | |
if (p.getX() != polyPoints[i] || p.getY() != polyPoints[i + 1]) { | |
return false; | |
} | |
i += 2; | |
} | |
return true; | |
} | |
/** | |
* Sets the location of the channel decorator. | |
* | |
* @param con | |
* The connection containing the channel decorator. | |
* @param location | |
* The relative position of the decorator. | |
* @param diagram | |
* The diagram object. | |
*/ | |
public void setChannelLocation(Connection con, Diagram diagram, | |
double location) { | |
ConnectionDecorator channelDecorator = getChannelDecorator(con); | |
if (channelDecorator != null) { | |
Set<ConnectionDecorator> arrowDecorators = getArrowDecorators(con); | |
for (ConnectionDecorator arrowDecorator : arrowDecorators) { | |
if (arrowDecorator.getLocation() != 1.0 | |
&& arrowDecorator.getLocation() != 0.0) { | |
arrowDecorator.setLocation(location); | |
} | |
} | |
con.getConnectionDecorators().remove(channelDecorator); | |
ConnectionDecorator newChannelDecorator = createChannelSymbol(con, | |
diagram); | |
newChannelDecorator.setLocation(location); | |
ConnectionDecorator requestRDecorator = getRequestRDecorator(con); | |
if (requestRDecorator != null) | |
requestRDecorator.setLocation(location); | |
} | |
} | |
/** | |
* Returns the relative location of the channel decorator. | |
* | |
* @param con The connection containing the decorator. | |
* @return The relative channel location. | |
*/ | |
public double getChannelLocation(Connection con) { | |
ConnectionDecorator channelDecorator = getChannelDecorator(con); | |
if (channelDecorator != null) { | |
return channelDecorator.getLocation(); | |
} else | |
return 0; | |
} | |
/** | |
* | |
* @param con | |
* @param direction | |
* @param diagram | |
*/ | |
public void setRequestDirection(Connection con, RequestDirection direction, | |
Diagram diagram) { | |
Set<ConnectionDecorator> arrows = getArrowDecorators(con); | |
DataflowDirection dfDirection = getDirection(con); | |
con.getConnectionDecorators().removeAll(arrows); | |
con.getConnectionDecorators().remove(getRequestRDecorator(con)); | |
createRequestArrow(con, direction, diagram); | |
createDirectionArrow(con, dfDirection, diagram); | |
} | |
/** | |
* Return the request direction of the communication channel, either | |
* Request, Response or Request/Response. | |
* | |
* @param con | |
* The connection representing a communication channel. | |
* @return The request direction of the communication channel. | |
*/ | |
public RequestDirection getRequestDirection(Connection con) { | |
boolean request = false; | |
boolean response = false; | |
for (ConnectionDecorator deco : con.getConnectionDecorators()) { | |
if (deco.getGraphicsAlgorithm() instanceof Polygon) { | |
if (equalsPolygon(POINTS_REQUEST_ARROW, | |
(Polygon) deco.getGraphicsAlgorithm())) { | |
request = true; | |
} | |
if (equalsPolygon(POINTS_RESPONSE_ARROW, | |
(Polygon) deco.getGraphicsAlgorithm())) { | |
response = true; | |
} | |
if (request && response) | |
return RequestDirection.REQUESTRESPONSE; | |
} | |
} | |
if (request) { | |
return RequestDirection.REQUEST; | |
} else if (response) { | |
return RequestDirection.RESPONSE; | |
} | |
return RequestDirection.UNSPECIFIED; | |
} | |
/** | |
* Creates a new request arrow with the specified request direction. | |
* | |
* @param con | |
* Then connection for which the decorator will be created. | |
* @param direction | |
* The request arrow direction | |
* @param diagram | |
* The diagram containing the connection. | |
*/ | |
private void createRequestArrow(Connection con, RequestDirection direction, | |
Diagram diagram) { | |
IGaService ga = Graphiti.getGaService(); | |
IPeService pe = Graphiti.getPeService(); | |
ConnectionDecorator arrow = pe.createConnectionDecorator(con, false, | |
decoPosX, true); | |
Polygon arrowPoly = null; | |
switch (direction) { | |
case REQUEST: | |
arrowPoly = ga.createPolygon(arrow, POINTS_REQUEST_ARROW); | |
arrowPoly.setBackground(ga.manageColor(diagram, 0, 0, 0)); | |
break; | |
case RESPONSE: | |
arrowPoly = ga.createPolygon(arrow, POINTS_RESPONSE_ARROW); | |
arrowPoly.setBackground(ga.manageColor(diagram, 0, 0, 0)); | |
break; | |
case REQUESTRESPONSE: | |
arrowPoly = ga.createPolygon(arrow, POINTS_REQUEST_ARROW); | |
arrowPoly.setBackground(ga.manageColor(diagram, 0, 0, 0)); | |
ConnectionDecorator arrow2 = pe.createConnectionDecorator(con, | |
false, decoPosX, true); | |
Polygon arrowPoly2 = ga | |
.createPolygon(arrow2, POINTS_RESPONSE_ARROW); | |
arrowPoly2.setBackground(ga.manageColor(diagram, 0, 0, 0)); | |
break; | |
default: | |
break; | |
} | |
if (direction != RequestDirection.UNSPECIFIED) { | |
ConnectionDecorator letter = pe.createConnectionDecorator(con, | |
false, decoPosX, true); | |
letter.setActive(true); | |
Text txt = ga.createText(letter, R_DECO); | |
txt.setForeground(ga.manageColor(diagram, 0, 0, 0)); | |
txt.setWidth(10); | |
txt.setHeight(10); | |
ga.setLocation(txt, -8, -22); | |
} | |
} | |
/** | |
* Sets the data flow direction of a connection by creating a connection | |
* decorator. If the connection is a composite connection, the direction | |
* is also switched for the children. | |
* | |
* @param con | |
* The connection to set the data flow direction for. | |
* @param direction | |
* The data flow direction to be set. | |
* @param diagram | |
* The diagram containing the connection. | |
*/ | |
public void setDirection(Connection con, DataflowDirection direction, | |
Diagram diagram) { | |
if (con instanceof CompositeConnection) { | |
CompositeConnection composite = (CompositeConnection) con; | |
int i = 0; | |
for (CurvedConnection child : composite.getChildren()) { | |
changeDirection(child, i == 0 ? direction | |
: DataflowDirection.getOpposite(direction), diagram); | |
i++; | |
} | |
} else | |
changeDirection(con, direction, diagram); | |
} | |
/** | |
* Sets the data flow direction of a connection by creating a connection | |
* decorator. | |
* | |
* @param con | |
* The connection to set the data flow direction for. | |
* @param direction | |
* The data flow direction to be set. | |
* @param diagram | |
* The diagram containing the connection. | |
*/ | |
private void changeDirection(Connection con, DataflowDirection direction, | |
Diagram diagram) { | |
Set<ConnectionDecorator> arrows = getArrowDecorators(con); | |
RequestDirection requestDirection = getRequestDirection(con); | |
con.getConnectionDecorators().removeAll(arrows); | |
con.getConnectionDecorators().remove(getRequestRDecorator(con)); | |
createDirectionArrow(con, direction, diagram); | |
createRequestArrow(con, requestDirection, diagram); | |
} | |
/** | |
* Returns the data flow direction of the connection. | |
* | |
* @param con | |
* The connection for which the data flow is to be determined. | |
* @return The data flow direction of the connection. | |
*/ | |
public DataflowDirection getDirection(Connection con) { | |
Set<ConnectionDecorator> arrows = getArrowDecorators(con); | |
if (arrows.isEmpty()) | |
return DataflowDirection.UNSPECIFIED; | |
else { | |
for (ConnectionDecorator connectionDecorator : arrows) { | |
if (connectionDecorator.getLocation() == 0) { | |
return DataflowDirection.OTHER; | |
} else if (connectionDecorator.getLocation() == 1) { | |
return DataflowDirection.DEFAULT; | |
} | |
} | |
return DataflowDirection.UNSPECIFIED; | |
} | |
} | |
/** | |
* Creates a data flow direction arrow for the connection. | |
* | |
* @param con | |
* The connection to create the connection for. | |
* @param direction | |
* The direction of the connection to be created. | |
* @param diagram | |
* The diagram containing the connection. | |
*/ | |
private void createDirectionArrow(Connection con, | |
DataflowDirection direction, Diagram diagram) { | |
boolean defaultDirection = direction == DataflowDirection.DEFAULT; | |
if (direction != DataflowDirection.UNSPECIFIED) { | |
ConnectionAddFeature.createArrowDecorator(diagram, con, | |
defaultDirection); | |
} | |
if (direction != DataflowDirection.UNSPECIFIED | |
&& helper.isCommunicationChannel(con)) { | |
CommunicationChannelAddFeature.createCenterArrow(diagram, con, | |
defaultDirection); | |
} | |
} | |
/** | |
* Creates a channel connection decorator for the connection. | |
* | |
* @param con | |
* The connection to create a channel for. | |
* @param diagram | |
* The diagram containing the connection. | |
* @return The connection decorator for the channel. | |
*/ | |
public ConnectionDecorator createChannelSymbol(Connection con, | |
Diagram diagram) { | |
ConnectionDecorator cDecorator = Graphiti.getPeService() | |
.createConnectionDecorator(con, false, decoPosX, true); | |
Ellipse e = Graphiti.getGaService().createEllipse(cDecorator); | |
e.setBackground(Graphiti.getGaService().manageColor(diagram, 255, 255, | |
255)); | |
e.setForeground(Graphiti.getGaService().manageColor(diagram, 0, 0, 0)); | |
e.setLineWidth(2); | |
Graphiti.getGaService().setLocationAndSize(e, -8, 0, CHANNEL_MIN_SIZE, | |
CHANNEL_MIN_SIZE); | |
return cDecorator; | |
} | |
} |