/*******************************************************************************
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* 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
*******************************************************************************/
package gov.redhawk.core.graphiti.ui.diagram.patterns;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.graphiti.features.context.IAddConnectionContext;
import org.eclipse.graphiti.features.context.IAddContext;
import org.eclipse.graphiti.features.context.IConnectionContext;
import org.eclipse.graphiti.features.context.ICreateConnectionContext;
import org.eclipse.graphiti.features.context.impl.AddConnectionContext;
import org.eclipse.graphiti.mm.MmFactory;
import org.eclipse.graphiti.mm.Property;
import org.eclipse.graphiti.mm.algorithms.Polygon;
import org.eclipse.graphiti.mm.algorithms.Polyline;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.Connection;
import org.eclipse.graphiti.mm.pictograms.ConnectionDecorator;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.pattern.AbstractConnectionPattern;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.services.IGaService;
import gov.redhawk.core.graphiti.ui.diagram.providers.AbstractToolBehaviorProvider;
import gov.redhawk.core.graphiti.ui.diagram.providers.ImageProvider;
import gov.redhawk.core.graphiti.ui.ext.RHContainerShape;
import gov.redhawk.core.graphiti.ui.util.DUtil;
import gov.redhawk.core.graphiti.ui.util.StyleUtil;
import gov.redhawk.sca.util.StringUtil;
import mil.jpeojtrs.sca.partitioning.ConnectInterface;
import mil.jpeojtrs.sca.partitioning.ConnectionTarget;
import mil.jpeojtrs.sca.partitioning.Connections;
import mil.jpeojtrs.sca.partitioning.FindBy;
import mil.jpeojtrs.sca.partitioning.UsesPortStub;
import mil.jpeojtrs.sca.util.ScaEcoreUtils;
public abstract class AbstractConnectInterfacePattern extends AbstractConnectionPattern {
public static final String NAME = "Connection";
public static final String SHAPE_IMG_CONNECTION_DECORATOR = "imgConnectionDecorator";
public static final String SHAPE_TEXT_CONNECTION_DECORATOR = "textConnectionDecorator";
public static final String OVERRIDE_CONNECTION_ID = "OverrideConnectionId";
public static final String CONNECT_INTERFACE_ID = "connect_interface_id";
private Anchor sourceAnchor;
@Override
public String getCreateName() {
return NAME;
}
@Override
public String getCreateDescription() {
return "Create new Connect Interface";
}
@Override
public String getCreateImageId() {
return ImageProvider.IMG_CONNECTION;
}
@Override
public boolean canStartConnection(ICreateConnectionContext context) {
// Not allowed in the explorer or if read-only
Diagram diagram = getDiagram();
if (DUtil.isDiagramExplorer(diagram) || DUtil.isDiagramReadOnly(diagram)) {
return false;
}
// Check if the source anchor belongs to a component, and disallow the connection if it's disabled
RHContainerShape component = ScaEcoreUtils.getEContainerOfType(context.getSourceAnchor(), RHContainerShape.class);
if (component != null && !component.isEnabled()) {
return false;
}
// We must be able to find a UsesPortStub or ConnectionTarget
if (getUsesPortStub(context) != null || getConnectionTarget(context) != null) {
sourceAnchor = context.getSourceAnchor();
return true;
}
return false;
}
protected AbstractToolBehaviorProvider getToolBehaviorProvider() {
return (AbstractToolBehaviorProvider) getFeatureProvider().getDiagramTypeProvider().getCurrentToolBehaviorProvider();
}
@Override
public void startConnecting() {
// Highlight ports that may be valid for completing the connection
getToolBehaviorProvider().startConnectionHighlighting(sourceAnchor);
}
@Override
public boolean canCreate(ICreateConnectionContext context) {
// Check if the target anchor belongs to a component, and disallow the connection if it's disabled; the source
// was already checked in canStart().
RHContainerShape component = ScaEcoreUtils.getEContainerOfType(context.getTargetAnchor(), RHContainerShape.class);
if (component != null && !component.isEnabled()) {
return false;
}
// In any other case, assume it's a valid connection so that the mouse pointer doesn't give negative feedback.
return true;
}
@Override
public PictogramElement add(IAddContext addContext) {
IAddConnectionContext context = (IAddConnectionContext) addContext;
// source and target
UsesPortStub source = getUsesPortStub(context);
ConnectionTarget target = getConnectionTarget(context);
// Create connection (handle user selecting source or target)
Connection connectionPE = Graphiti.getPeCreateService().createFreeFormConnection(getFeatureProvider().getDiagramTypeProvider().getDiagram());
if (source == getUsesPortStub(context.getSourceAnchor()) && target == getConnectionTarget(context.getTargetAnchor())) {
connectionPE.setStart(context.getSourceAnchor());
connectionPE.setEnd(context.getTargetAnchor());
} else if (source == getUsesPortStub(context.getTargetAnchor()) && target == getConnectionTarget(context.getSourceAnchor())) {
connectionPE.setStart(context.getTargetAnchor());
connectionPE.setEnd(context.getSourceAnchor());
}
// create line
IGaService gaService = Graphiti.getGaService();
Polyline line = gaService.createPlainPolyline(connectionPE);
StyleUtil.setStyle(line, StyleUtil.CONNECTION);
// Add arrow (as a decorator) to the end of the line
ConnectionDecorator arrowDecorator = Graphiti.getPeCreateService().createConnectionDecorator(connectionPE, false, 1.0, true);
Polygon polyArrow = gaService.createPlainPolygon(arrowDecorator, new int[] { -10, 5, 0, 0, -10, -5 });
StyleUtil.setStyle(polyArrow, StyleUtil.CONNECTION);
// link ports to connection
getFeatureProvider().link(connectionPE, new Object[] { context.getNewObject(), source, target });
return connectionPE;
}
/**
* Override in subclass to return an instance of the specific ConnectInterface type
*/
protected abstract ConnectInterface< ? , ? , ? > createConnectInterface();
/**
* Override in subclass to return a unique connection ID for the SAD/DCD
*/
protected abstract String createConnectionId();
/**
* Override in subclass to add the ConnectInterface to the SAD/DCD
*/
protected abstract void addConnectInterface(ConnectInterface< ? , ? , ? > connection);
protected ConnectInterface< ? , ? , ? > createModelConnection(Anchor source, Anchor target) {
ConnectInterface< ? , ? , ? > connection = createConnectInterface();
boolean success = DUtil.assignAnchorObjectsToConnection(connection, source, target);
return (success) ? connection : null;
}
/**
* Creates a new connection between the selected usesPortStub and ConnectionTarget
*/
@Override
public Connection create(ICreateConnectionContext context) {
// There appears to be a bug in Graphiti/GMF where endConnecting() does not get called if this method opens a
// dialog (e.g., a connection to/from a super port with more than one potential match); explicitly calling it
// here will clear the port highlighting.
endConnecting();
// Attempt to create the connection, trying both directions if necessary
final ConnectInterface< ? , ? , ? > modelConnection = createModelConnection(context.getSourceAnchor(), context.getTargetAnchor());
if (modelConnection == null) {
return null;
}
// Allow the caller to override the connection ID (e.g, building a diagram from runtime state)
String connectionId = (String) context.getProperty(OVERRIDE_CONNECTION_ID);
if (connectionId == null) {
connectionId = createConnectionId();
}
modelConnection.setId(connectionId);
// If we just created some findBy model objects, make sure they are referenced by the pictogram element
FindBy fb = null;
RHContainerShape findByPE = null;
if (modelConnection.getProvidesPort() == null) {
fb = modelConnection.getComponentSupportedInterface().getFindBy();
} else {
fb = modelConnection.getProvidesPort().getFindBy();
}
if (fb == null) {
fb = modelConnection.getUsesPort().getFindBy();
if (fb != null) {
findByPE = ScaEcoreUtils.getEContainerOfType(context.getSourceAnchor(), RHContainerShape.class);
}
} else {
findByPE = ScaEcoreUtils.getEContainerOfType(context.getTargetAnchor(), RHContainerShape.class);
}
if (fb != null) {
Property connectInterfaceProp = MmFactory.eINSTANCE.createProperty();
connectInterfaceProp.setKey(CONNECT_INTERFACE_ID);
connectInterfaceProp.setValue(modelConnection.getId());
findByPE.getProperties().add(connectInterfaceProp);
}
// Add the connection to the SAD/DCD model
TransactionalEditingDomain editingDomain = getDiagramBehavior().getEditingDomain();
editingDomain.getCommandStack().execute(new RecordingCommand(editingDomain) {
@Override
protected void doExecute() {
addConnectInterface(modelConnection);
}
});
// Add diagram connection for business object
AddConnectionContext addContext = new AddConnectionContext(context.getSourceAnchor(), context.getTargetAnchor());
addContext.setNewObject(modelConnection);
return (Connection) getFeatureProvider().addIfPossible(addContext);
}
@Override
public void endConnecting() {
// Turns off highlighting ports for the connection
getToolBehaviorProvider().endConnectionHighlighting();
super.endConnecting();
}
/**
* Return UsesPortStub from either the source or target anchor. Depends on how user drew connection.
* @param context
* @return
*/
protected UsesPortStub getUsesPortStub(IConnectionContext context) {
UsesPortStub source = getUsesPortStub(context.getSourceAnchor());
if (source != null) {
return source;
}
source = getUsesPortStub(context.getTargetAnchor());
return source;
}
protected UsesPortStub getUsesPortStub(Anchor anchor) {
if (anchor != null) {
Object object = getBusinessObjectForPictogramElement(anchor.getParent());
if (object instanceof UsesPortStub) {
return (UsesPortStub) object;
}
}
return null;
}
/**
* Return ConnectionTarget from either the source or target anchor. Depends on how user drew connection.
* @param context
* @return
*/
protected ConnectionTarget getConnectionTarget(IConnectionContext context) {
ConnectionTarget connectionTarget = getConnectionTarget(context.getSourceAnchor());
if (connectionTarget != null) {
return connectionTarget;
}
connectionTarget = getConnectionTarget(context.getTargetAnchor());
return connectionTarget;
}
protected ConnectionTarget getConnectionTarget(Anchor anchor) {
if (anchor != null) {
Object object = getBusinessObjectForPictogramElement(anchor.getParent());
if (object instanceof ConnectionTarget) {
return (ConnectionTarget) object;
}
}
return null;
}
/**
* Returns the next available connection id
*/
protected String createConnectionId(Connections< ? > connections) {
final List<String> ids = new ArrayList<String>();
if (connections != null) {
for (final ConnectInterface< ? , ? , ? > connection : connections.getConnectInterface()) {
ids.add(connection.getId());
}
}
return StringUtil.defaultCreateUniqueString("connection_1", ids);
}
}