blob: 0e97347c5c6afd5dcdb8519a6fa100f150cf3651 [file] [log] [blame]
/*******************************************************************************
* <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;
}
}