blob: 181c0917c0c0f2524b68680dc93b57db78e03c2a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 Red Hat, Inc.
* All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
*
* @author Ivar Meikas
******************************************************************************/
package org.eclipse.bpmn2.modeler.core.features;
import java.io.IOException;
import org.eclipse.bpmn2.Association;
import org.eclipse.bpmn2.BaseElement;
import org.eclipse.bpmn2.EndEvent;
import org.eclipse.bpmn2.Lane;
import org.eclipse.bpmn2.MessageFlow;
import org.eclipse.bpmn2.Participant;
import org.eclipse.bpmn2.SequenceFlow;
import org.eclipse.bpmn2.StartEvent;
import org.eclipse.bpmn2.di.BPMNDiagram;
import org.eclipse.bpmn2.di.BPMNEdge;
import org.eclipse.bpmn2.di.BPMNShape;
import org.eclipse.bpmn2.di.BpmnDiFactory;
import org.eclipse.bpmn2.modeler.core.Activator;
import org.eclipse.bpmn2.modeler.core.di.DIImport;
import org.eclipse.bpmn2.modeler.core.di.DIUtils;
import org.eclipse.bpmn2.modeler.core.features.flow.AbstractCreateFlowFeature;
import org.eclipse.bpmn2.modeler.core.preferences.Bpmn2Preferences;
import org.eclipse.bpmn2.modeler.core.utils.AnchorUtil;
import org.eclipse.bpmn2.modeler.core.utils.BusinessObjectUtil;
import org.eclipse.bpmn2.modeler.core.utils.FeatureSupport;
import org.eclipse.bpmn2.modeler.core.utils.GraphicsUtil;
import org.eclipse.bpmn2.modeler.core.utils.ModelUtil;
import org.eclipse.dd.dc.Bounds;
import org.eclipse.dd.dc.DcFactory;
import org.eclipse.dd.dc.Point;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.graphiti.IExecutionInfo;
import org.eclipse.graphiti.datatypes.ILocation;
import org.eclipse.graphiti.features.ICreateConnectionFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.IReason;
import org.eclipse.graphiti.features.IReconnectionFeature;
import org.eclipse.graphiti.features.context.IAddContext;
import org.eclipse.graphiti.features.context.ITargetContext;
import org.eclipse.graphiti.features.context.impl.AddContext;
import org.eclipse.graphiti.features.context.impl.CreateConnectionContext;
import org.eclipse.graphiti.features.context.impl.LayoutContext;
import org.eclipse.graphiti.features.context.impl.ReconnectionContext;
import org.eclipse.graphiti.features.context.impl.UpdateContext;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.AnchorContainer;
import org.eclipse.graphiti.mm.pictograms.Connection;
import org.eclipse.graphiti.mm.pictograms.ContainerShape;
import org.eclipse.graphiti.mm.pictograms.FixPointAnchor;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.mm.pictograms.Shape;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.services.ILayoutService;
public abstract class AbstractBpmn2AddElementFeature<T extends BaseElement>
extends AbstractBpmn2AddFeature<T> {
public AbstractBpmn2AddElementFeature(IFeatureProvider fp) {
super(fp);
}
protected BPMNShape findDIShape(BaseElement elem) {
try {
return DIUtils.findBPMNShape(elem);
} catch (Exception e) {
Activator.logError(e);
}
return null;
}
protected BPMNShape createDIShape(Shape gShape, BaseElement elem, boolean applyDefaults) {
return createDIShape(gShape, elem, findDIShape(elem), applyDefaults);
}
protected BPMNShape createDIShape(Shape shape, BaseElement elem, BPMNShape bpmnShape, boolean applyDefaults) {
ILocation loc = Graphiti.getLayoutService().getLocationRelativeToDiagram(shape);
if (bpmnShape == null) {
int x = loc.getX();
int y = loc.getY();
int w = shape.getGraphicsAlgorithm().getWidth();
int h = shape.getGraphicsAlgorithm().getHeight();
bpmnShape = DIUtils.createDIShape(shape, elem, x, y, w, h, getFeatureProvider(), getDiagram());
}
else {
link(shape, new Object[] { elem, bpmnShape });
}
if (applyDefaults && bpmnShape!=null)
Bpmn2Preferences.getInstance(bpmnShape.eResource()).applyBPMNDIDefaults(bpmnShape, null);
return bpmnShape;
}
protected BPMNEdge createDIEdge(Connection connection, BaseElement conElement) {
try {
BPMNEdge edge = DIUtils.findBPMNEdge(conElement);
return createDIEdge(connection, conElement, edge);
} catch (IOException e) {
Activator.logError(e);
}
return null;
}
// TODO: move this to DIUtils
protected BPMNEdge createDIEdge(Connection connection, BaseElement conElement, BPMNEdge edge) throws IOException {
if (edge == null) {
EList<EObject> businessObjects = Graphiti.getLinkService().getLinkForPictogramElement(getDiagram())
.getBusinessObjects();
for (EObject eObject : businessObjects) {
if (eObject instanceof BPMNDiagram) {
BPMNDiagram bpmnDiagram = (BPMNDiagram) eObject;
edge = BpmnDiFactory.eINSTANCE.createBPMNEdge();
// edge.setId(EcoreUtil.generateUUID());
edge.setBpmnElement(conElement);
if (conElement instanceof Association) {
edge.setSourceElement(DIUtils.findDiagramElement(
((Association) conElement).getSourceRef()));
edge.setTargetElement(DIUtils.findDiagramElement(
((Association) conElement).getTargetRef()));
} else if (conElement instanceof MessageFlow) {
edge.setSourceElement(DIUtils.findDiagramElement(
(BaseElement) ((MessageFlow) conElement).getSourceRef()));
edge.setTargetElement(DIUtils.findDiagramElement(
(BaseElement) ((MessageFlow) conElement).getTargetRef()));
} else if (conElement instanceof SequenceFlow) {
edge.setSourceElement(DIUtils.findDiagramElement(
((SequenceFlow) conElement).getSourceRef()));
edge.setTargetElement(DIUtils.findDiagramElement(
((SequenceFlow) conElement).getTargetRef()));
}
ILocation sourceLoc = Graphiti.getPeService().getLocationRelativeToDiagram(connection.getStart());
ILocation targetLoc = Graphiti.getPeService().getLocationRelativeToDiagram(connection.getEnd());
Point point = DcFactory.eINSTANCE.createPoint();
point.setX(sourceLoc.getX());
point.setY(sourceLoc.getY());
edge.getWaypoint().add(point);
point = DcFactory.eINSTANCE.createPoint();
point.setX(targetLoc.getX());
point.setY(targetLoc.getY());
edge.getWaypoint().add(point);
DIUtils.addShape(edge, bpmnDiagram);
ModelUtil.setID(edge);
}
}
}
link(connection, new Object[] { conElement, edge });
return edge;
}
protected void prepareAddContext(IAddContext context, PictogramElement labelOwner, int width, int height) {
context.putProperty(ContextConstants.LABEL_CONTEXT, true);
context.putProperty(ContextConstants.WIDTH, width);
context.putProperty(ContextConstants.HEIGHT, height);
context.putProperty(ContextConstants.BUSINESS_OBJECT, getBusinessObject(context));
context.putProperty(ContextConstants.LABEL_OWNER, labelOwner);
}
protected void adjustLocation(IAddContext context, int width, int height) {
if (context.getProperty(DIImport.IMPORT_PROPERTY) != null) {
return;
}
int x = context.getX();
int y = context.getY();
((AddContext)context).setWidth(width);
((AddContext)context).setHeight(height);
y -= height/2;
x -= width / 2;
((AddContext)context).setY(y);
((AddContext)context).setX(x);
}
protected void splitConnection(IAddContext context, ContainerShape containerShape) {
if (context.getProperty(DIImport.IMPORT_PROPERTY) != null) {
return;
}
Object newObject = getBusinessObject(context);
Connection connection = context.getTargetConnection();
if (connection!=null) {
// determine how to split the line depending on where the new object was dropped:
// the longer segment will remain the original connection, and a new connection
// will be created for the shorter segment
ILayoutService layoutService = Graphiti.getLayoutService();
Anchor a0 = connection.getStart();
Anchor a1 = connection.getEnd();
double x0 = layoutService.getLocationRelativeToDiagram(a0).getX();
double y0 = layoutService.getLocationRelativeToDiagram(a0).getY();
double x1 = layoutService.getLocationRelativeToDiagram(a1).getX();
double y1 = layoutService.getLocationRelativeToDiagram(a1).getY();
double dx = x0 - context.getX();
double dy = y0 - context.getY();
double len0 = Math.sqrt(dx*dx + dy*dy);
dx = context.getX() - x1;
dy = context.getY() - y1;
double len1 = Math.sqrt(dx*dx + dy*dy);
AnchorContainer oldSourceContainer = connection.getStart().getParent();
AnchorContainer oldTargetContainer = connection.getEnd().getParent();
BaseElement baseElement = BusinessObjectUtil.getFirstElementOfType(connection, BaseElement.class);
ILocation targetLocation = layoutService.getLocationRelativeToDiagram(containerShape);
ReconnectionContext rc;
FixPointAnchor anchor;
if (newObject instanceof StartEvent || (len0 < len1 && !(newObject instanceof EndEvent))) {
anchor = AnchorUtil.findNearestAnchor(containerShape, GraphicsUtil.getShapeCenter(oldTargetContainer));
rc = new ReconnectionContext(connection, connection.getStart(), anchor, targetLocation);
rc.setReconnectType(ReconnectionContext.RECONNECT_SOURCE);
rc.setTargetPictogramElement(containerShape);
}
else {
anchor = AnchorUtil.findNearestAnchor(oldTargetContainer, GraphicsUtil.getShapeCenter(containerShape));
rc = new ReconnectionContext(connection, connection.getEnd(), anchor, targetLocation);
rc.setReconnectType(ReconnectionContext.RECONNECT_TARGET);
rc.setTargetPictogramElement(containerShape);
}
IReconnectionFeature rf = getFeatureProvider().getReconnectionFeature(rc);
rf.reconnect(rc);
if (!(newObject instanceof EndEvent) && !(newObject instanceof StartEvent)) {
// connection = get create feature, create connection
CreateConnectionContext ccc = new CreateConnectionContext();
if (len0 < len1) {
ccc.setSourcePictogramElement(oldSourceContainer);
ccc.setTargetPictogramElement(containerShape);
anchor = AnchorUtil.findNearestAnchor(oldSourceContainer, GraphicsUtil.getShapeCenter(containerShape));
ccc.setSourceAnchor(anchor);
anchor = AnchorUtil.findNearestAnchor(containerShape, GraphicsUtil.getShapeCenter(oldTargetContainer));
ccc.setTargetAnchor(anchor);
}
else {
ccc.setSourcePictogramElement(containerShape);
ccc.setTargetPictogramElement(oldTargetContainer);
anchor = AnchorUtil.findNearestAnchor(containerShape, GraphicsUtil.getShapeCenter(oldTargetContainer));
ccc.setSourceAnchor(anchor);
anchor = AnchorUtil.findNearestAnchor(oldTargetContainer, GraphicsUtil.getShapeCenter(containerShape));
ccc.setTargetAnchor(anchor);
}
Connection newConnection = null;
for (ICreateConnectionFeature cf : getFeatureProvider().getCreateConnectionFeatures()) {
if (cf instanceof AbstractCreateFlowFeature) {
AbstractCreateFlowFeature acf = (AbstractCreateFlowFeature) cf;
if (acf.getBusinessObjectClass().isInstance(baseElement)) {
newConnection = acf.create(ccc);
DIUtils.updateDIEdge(newConnection);
break;
}
}
}
}
DIUtils.updateDIEdge(connection);
}
}
protected int getHeight(IAddContext context) {
Object copiedBpmnShape = context.getProperty(DefaultPasteBPMNElementFeature.COPIED_BPMN_SHAPE);
if (copiedBpmnShape instanceof BPMNShape) {
Bounds b = ((BPMNShape)copiedBpmnShape).getBounds();
if (b!=null)
return (int) b.getHeight();
}
return context.getHeight() > 0 ? context.getHeight() :
(isHorizontal(context) ? getHeight() : getWidth());
}
protected int getWidth(IAddContext context) {
Object copiedBpmnShape = context.getProperty(DefaultPasteBPMNElementFeature.COPIED_BPMN_SHAPE);
if (copiedBpmnShape instanceof BPMNShape) {
Bounds b = ((BPMNShape)copiedBpmnShape).getBounds();
if (b!=null)
return (int) b.getWidth();
}
return context.getWidth() > 0 ? context.getWidth() :
(isHorizontal(context) ? getWidth() : getHeight());
}
protected boolean isHorizontal(ITargetContext context) {
if (context.getProperty(DIImport.IMPORT_PROPERTY) == null) {
// not importing - set isHorizontal to be the same as copied element or parent
Object copiedBpmnShape = context.getProperty(DefaultPasteBPMNElementFeature.COPIED_BPMN_SHAPE);
if (copiedBpmnShape instanceof BPMNShape) {
return ((BPMNShape)copiedBpmnShape).isIsHorizontal();
}
if (FeatureSupport.isTargetParticipant(context)) {
Participant targetParticipant = FeatureSupport.getTargetParticipant(context);
BPMNShape participantShape = findDIShape(targetParticipant);
if (participantShape!=null)
return participantShape.isIsHorizontal();
}
else if (FeatureSupport.isTargetLane(context)) {
Lane targetLane = FeatureSupport.getTargetLane(context);
BPMNShape laneShape = findDIShape(targetLane);
if (laneShape!=null)
return laneShape.isIsHorizontal();
}
}
return Bpmn2Preferences.getInstance(context.getTargetContainer()).isHorizontalDefault();
}
public abstract int getHeight();
public abstract int getWidth();
@Override
public T getBusinessObject(IAddContext context) {
Object businessObject = context.getProperty(ContextConstants.BUSINESS_OBJECT);
if (businessObject instanceof BaseElement)
return (T)businessObject;
return (T)context.getNewObject();
}
@Override
public void putBusinessObject(IAddContext context, T businessObject) {
context.putProperty(ContextConstants.BUSINESS_OBJECT, businessObject);
}
@Override
public void postExecute(IExecutionInfo executionInfo) {
}
/**
* Update the given PictogramElement. A Graphiti UpdateContext is constructed by copying
* the properties from the given AddContext.
*
* @param addContext - the Graphiti AddContext that was used to add the PE to the Diagram
* @param pe - the PictogramElement
* @return a reason code indicating whether or not an update is needed.
*/
protected IReason updatePictogramElement(IAddContext addContext, PictogramElement pe) {
UpdateContext updateContext = new UpdateContext(pe);
for (Object key : addContext.getPropertyKeys()) {
Object value = addContext.getProperty(key);
updateContext.putProperty(key, value);
}
return getFeatureProvider().updateIfPossible(updateContext);
}
/**
* Layout the given PictogramElement. A Graphiti LayoutContext is constructed by copying
* the properties from the given AddContext.
*
* @param addContext - the Graphiti AddContext that was used to add the PE to the Diagram
* @param pe - the PictogramElement
* @return a reason code indicating whether or not a layout is needed.
*/
protected IReason layoutPictogramElement(IAddContext addContext, PictogramElement pe) {
LayoutContext layoutContext = new LayoutContext(pe);
for (Object key : addContext.getPropertyKeys()) {
Object value = addContext.getProperty(key);
layoutContext.putProperty(key, value);
}
return getFeatureProvider().layoutIfPossible(layoutContext);
}
}