blob: 290c21e32ef3eb1a349849b3228eef566e08c896 [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.util.ArrayList;
import java.util.List;
import org.eclipse.bpmn2.BaseElement;
import org.eclipse.bpmn2.Bpmn2Factory;
import org.eclipse.bpmn2.FlowElementsContainer;
import org.eclipse.bpmn2.FlowNode;
import org.eclipse.bpmn2.Lane;
import org.eclipse.bpmn2.MessageFlow;
import org.eclipse.bpmn2.Participant;
import org.eclipse.bpmn2.SequenceFlow;
import org.eclipse.bpmn2.SubProcess;
import org.eclipse.bpmn2.modeler.core.Activator;
import org.eclipse.bpmn2.modeler.core.model.ModelHandler;
import org.eclipse.bpmn2.modeler.core.utils.BusinessObjectUtil;
import org.eclipse.bpmn2.modeler.core.utils.FeatureSupport;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.graphiti.features.IDeleteFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.IResizeShapeFeature;
import org.eclipse.graphiti.features.context.IMoveShapeContext;
import org.eclipse.graphiti.features.context.impl.DeleteContext;
import org.eclipse.graphiti.features.context.impl.ResizeShapeContext;
import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm;
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.PictogramElement;
import org.eclipse.graphiti.mm.pictograms.Shape;
// TODO: Auto-generated Javadoc
/**
* The Class MoveFlowNodeFeature.
*/
public class MoveFlowNodeFeature extends DefaultMoveBPMNShapeFeature {
private final List<Algorithm> algorithms;
private AlgorithmContainer algorithmContainer;
protected ModelHandler modelHandler;
/** The context. */
protected IMoveShapeContext context;
/**
* Instantiates a new move flow node feature.
*
* @param fp the fp
*/
public MoveFlowNodeFeature(IFeatureProvider fp) {
super(fp);
algorithms = new ArrayList<MoveFlowNodeFeature.Algorithm>();
algorithms.add(new FromLaneAlgorithm());
algorithms.add(new ToLaneAlgorithm());
algorithms.add(new FromParticipantAlgorithm());
algorithms.add(new ToParticipantAlgorithm());
algorithms.add(new FromFlowElementsContainerAlgorithm());
algorithms.add(new ToFlowElementsContainerAlgorithm());
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.DefaultMoveBPMNShapeFeature#canMoveShape(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
public boolean canMoveShape(IMoveShapeContext context) {
if (!(getBusinessObjectForPictogramElement(context.getShape()) instanceof FlowNode)) {
return false;
}
algorithmContainer = getAlgorithmContainer(context);
if (algorithmContainer.isEmpty()) {
return onMoveAlgorithmNotFound(context);
}
modelHandler = ModelHandler.getInstance(getDiagram());
return algorithmContainer.isMoveAllowed(getSourceBo(context), getTargetBo(context));
}
/**
* On move algorithm not found.
*
* @param context the context
* @return true, if successful
*/
protected boolean onMoveAlgorithmNotFound(IMoveShapeContext context) {
return super.canMoveShape(context);
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.DefaultMoveBPMNShapeFeature#moveShape(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
public void moveShape(IMoveShapeContext context) {
this.context = context;
super.moveShape(context);
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.DefaultMoveBPMNShapeFeature#postMoveShape(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
protected void postMoveShape(IMoveShapeContext context) {
Shape shape = context.getShape();
if (!FeatureSupport.isLabelShape(shape)) {
try {
Object[] nodes = getAllBusinessObjectsForPictogramElement(shape);
for (Object object : nodes) {
if (object instanceof FlowNode && algorithmContainer!=null && !algorithmContainer.isEmpty()) {
algorithmContainer.move(((FlowNode) object), getSourceBo(context), getTargetBo(context));
}
}
} catch (Exception e) {
Activator.logError(e);
}
ContainerShape parentContainer = shape.getContainer();
if (BusinessObjectUtil.containsElementOfType(parentContainer, SubProcess.class)) {
GraphicsAlgorithm ga = parentContainer.getGraphicsAlgorithm();
ResizeShapeContext resizeContext = new ResizeShapeContext(parentContainer);
resizeContext.setWidth(ga.getWidth());
resizeContext.setHeight(ga.getHeight());
resizeContext.setX(ga.getX());
resizeContext.setY(ga.getY());
IResizeShapeFeature resizeFeature = getFeatureProvider().getResizeShapeFeature(resizeContext);
resizeFeature.resizeShape(resizeContext);
}
}
super.postMoveShape(context);
}
private Object getSourceBo(IMoveShapeContext context) {
if (context.getSourceContainer().equals(getDiagram()))
return modelHandler.getFlowElementContainer(context.getSourceContainer());
return getBusinessObjectForPictogramElement(context.getSourceContainer());
}
private Object getTargetBo(IMoveShapeContext context) {
if (context.getTargetContainer().equals(getDiagram())) {
Object target = modelHandler.getFlowElementContainer(context.getTargetContainer());
if (target==null) {
// This handles the case where {@link #canMoveShape(IMoveShapeContext)} is called:
// at this point there is no write transaction open yet on the EditingDomain
// however, if the target is the Diagram but no default Process exists yet
// for that Diagram, then the move should be conditionally allowed.
return Bpmn2Factory.eINSTANCE.createProcess();
}
return target;
}
return getBusinessObjectForPictogramElement(context.getTargetContainer());
}
private boolean isSourceParticipant(IMoveShapeContext context) {
Object bo = getBusinessObjectForPictogramElement(context.getSourceContainer());
return context.getSourceContainer().equals(getDiagram()) || (bo != null && bo instanceof Participant);
}
private boolean isSourceLane(IMoveShapeContext context) {
Object bo = getBusinessObjectForPictogramElement(context.getSourceContainer());
return bo != null && bo instanceof Lane;
}
protected boolean checkConnectionAfterMove(Connection c) {
return true;
}
/**
* The Class AlgorithmContainer.
*/
class AlgorithmContainer {
/** The from algorithm. */
public Algorithm fromAlgorithm;
/** The to algorithm. */
public Algorithm toAlgorithm;
/**
* Instantiates a new algorithm container.
*
* @param fromAlgorithm the from algorithm
* @param toAlgorithm the to algorithm
*/
public AlgorithmContainer(Algorithm fromAlgorithm, Algorithm toAlgorithm) {
this.fromAlgorithm = fromAlgorithm;
this.toAlgorithm = toAlgorithm;
}
/**
* Checks if is move allowed.
*
* @param source the source
* @param target the target
* @return true, if is move allowed
*/
boolean isMoveAllowed(Object source, Object target) {
return fromAlgorithm.isMoveAllowed(source, target) && toAlgorithm.isMoveAllowed(source, target);
}
/**
* Move.
*
* @param node the node
* @param source the source
* @param target the target
*/
void move(FlowNode node, Object source, Object target) {
fromAlgorithm.move(node, source, target);
toAlgorithm.move(node, source, target);
// If this was a multiselection, keep all of the connections that
// were included in the move
PictogramElement pes[] = getDiagramEditor().getSelectedPictogramElements();
List<Connection> internalConnections = new ArrayList<Connection>();
for (PictogramElement pe : pes) {
if (pe instanceof AnchorContainer) {
for (Anchor a : ((AnchorContainer)pe).getAnchors()) {
for (Connection c : a.getIncomingConnections()) {
internalConnections.add(c);
}
for (Connection c : a.getOutgoingConnections()) {
internalConnections.add(c);
}
}
}
}
List<Connection> externalConnections = new ArrayList<Connection>();
for (Connection c : internalConnections) {
boolean foundSource = false;
boolean foundTarget = false;
for (PictogramElement p : pes) {
if (p==c.getStart().getParent())
foundSource = true;
if (p==c.getEnd().getParent())
foundTarget = true;
}
if (!foundSource || !foundTarget)
externalConnections.add(c);
}
internalConnections.removeAll(externalConnections);
// If flow node was moved from one Pool to another, delete all
// incoming and outgoing Sequence Flows; if flow node was connect
// to another flow node by a Message Flow, and it is moved into the
// same Pool as the other flow node, delete the Message Flows.
List<Connection> connections = new ArrayList<Connection>();
IFeatureProvider fp = MoveFlowNodeFeature.this.getFeatureProvider();
Shape shape = context.getShape();
for (Anchor a : shape.getAnchors()) {
for (Connection c : a.getIncomingConnections()) {
if (!internalConnections.contains(c) && !isConnectionValid(c))
connections.add(c);
}
for (Connection c : a.getOutgoingConnections()) {
if (!internalConnections.contains(c) && !isConnectionValid(c))
connections.add(c);
}
}
for (Connection c : connections) {
DeleteContext dc = new DeleteContext(c);
IDeleteFeature df = fp.getDeleteFeature(dc);
df.delete(dc);
}
}
/**
* Checks if is empty.
*
* @return true, if is empty
*/
boolean isEmpty() {
return fromAlgorithm == null || toAlgorithm == null;
}
/**
* Checks if is connection valid.
*
* @param flow the flow
* @return true, if is connection valid
*/
boolean isConnectionValid(Connection c) {
if (!MoveFlowNodeFeature.this.checkConnectionAfterMove(c))
return true;
BaseElement flow = BusinessObjectUtil.getFirstBaseElement(c);
if (!(flow instanceof SequenceFlow || flow instanceof MessageFlow))
return true;
EStructuralFeature sourceRef = flow.eClass().getEStructuralFeature("sourceRef"); //$NON-NLS-1$
EStructuralFeature targetRef = flow.eClass().getEStructuralFeature("targetRef"); //$NON-NLS-1$
BaseElement source = (BaseElement) flow.eGet(sourceRef);
BaseElement target = (BaseElement) flow.eGet(targetRef);
EObject sourceContainer = source.eContainer();
while (sourceContainer!=null) {
if (sourceContainer instanceof FlowElementsContainer)
break;
sourceContainer = sourceContainer.eContainer();
}
if (sourceContainer==null)
return true;
EObject targetContainer = target.eContainer();
while (targetContainer!=null) {
if (targetContainer instanceof FlowElementsContainer)
break;
targetContainer = targetContainer.eContainer();
}
if (targetContainer==null)
return true;
if (flow instanceof SequenceFlow)
return sourceContainer==targetContainer;
if (flow instanceof MessageFlow)
return sourceContainer!=targetContainer;
return false;
}
}
private AlgorithmContainer getAlgorithmContainer(IMoveShapeContext context) {
Algorithm fromAlgorithm = null;
Algorithm toAlgorithm = null;
for (Algorithm a : algorithms) {
if (a.canApplyTo(context)) {
switch (a.getType()) {
case Algorithm.TYPE_FROM:
fromAlgorithm = a;
break;
case Algorithm.TYPE_TO:
toAlgorithm = a;
break;
}
}
}
return new AlgorithmContainer(fromAlgorithm, toAlgorithm);
}
/**
* The Interface Algorithm.
*/
interface Algorithm {
/** The type from. */
int TYPE_FROM = 0;
/** The type to. */
int TYPE_TO = 1;
/**
* Gets the type.
*
* @return the type
*/
int getType();
/**
* Can apply to.
*
* @param context the context
* @return true, if successful
*/
boolean canApplyTo(IMoveShapeContext context);
/**
* Checks if is move allowed.
*
* @param source the source
* @param target the target
* @return true, if is move allowed
*/
boolean isMoveAllowed(Object source, Object target);
/**
* Move.
*
* @param node the node
* @param source the source
* @param target the target
*/
void move(FlowNode node, Object source, Object target);
}
/**
* The Class DefaultAlgorithm.
*/
abstract class DefaultAlgorithm implements Algorithm {
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#isMoveAllowed(java.lang.Object, java.lang.Object)
*/
@Override
public boolean isMoveAllowed(Object source, Object target) {
return true;
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#move(org.eclipse.bpmn2.FlowNode, java.lang.Object, java.lang.Object)
*/
@Override
public void move(FlowNode node, Object source, Object target) {
modelHandler.moveFlowNode(node, source, target);
}
}
/**
* The Class FromLaneAlgorithm.
*/
class FromLaneAlgorithm extends DefaultAlgorithm {
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#getType()
*/
@Override
public int getType() {
return TYPE_FROM;
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#canApplyTo(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
public boolean canApplyTo(IMoveShapeContext context) {
return isSourceLane(context);
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.DefaultAlgorithm#move(org.eclipse.bpmn2.FlowNode, java.lang.Object, java.lang.Object)
*/
@Override
public void move(FlowNode node, Object source, Object target) {
Lane lane = (Lane) source;
lane.getFlowNodeRefs().remove(node);
node.getLanes().remove(lane);
}
}
/**
* The Class ToLaneAlgorithm.
*/
class ToLaneAlgorithm extends DefaultAlgorithm {
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#getType()
*/
@Override
public int getType() {
return TYPE_TO;
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#canApplyTo(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
public boolean canApplyTo(IMoveShapeContext context) {
return FeatureSupport.isTargetLane(context);
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.DefaultAlgorithm#isMoveAllowed(java.lang.Object, java.lang.Object)
*/
@Override
public boolean isMoveAllowed(Object source, Object target) {
Lane lane = (Lane) target;
return lane.getChildLaneSet() == null || lane.getChildLaneSet().getLanes().isEmpty();
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.DefaultAlgorithm#move(org.eclipse.bpmn2.FlowNode, java.lang.Object, java.lang.Object)
*/
@Override
public void move(FlowNode node, Object source, Object target) {
Lane lane = (Lane) target;
lane.getFlowNodeRefs().add(node);
node.getLanes().add(lane);
super.move(node, source, target);
}
}
/**
* The Class FromParticipantAlgorithm.
*/
class FromParticipantAlgorithm extends DefaultAlgorithm {
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#getType()
*/
@Override
public int getType() {
return TYPE_FROM;
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#canApplyTo(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
public boolean canApplyTo(IMoveShapeContext context) {
return isSourceParticipant(context);
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.DefaultAlgorithm#move(org.eclipse.bpmn2.FlowNode, java.lang.Object, java.lang.Object)
*/
@Override
public void move(FlowNode node, Object source, Object target) {
// DO NOTHING HERE
}
}
/**
* The Class ToParticipantAlgorithm.
*/
class ToParticipantAlgorithm extends DefaultAlgorithm {
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#getType()
*/
@Override
public int getType() {
return TYPE_TO;
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#canApplyTo(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
public boolean canApplyTo(IMoveShapeContext context) {
return context.getTargetContainer().equals(getDiagram()) || FeatureSupport.isTargetParticipant(context);
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.DefaultAlgorithm#isMoveAllowed(java.lang.Object, java.lang.Object)
*/
@Override
public boolean isMoveAllowed(Object source, Object target) {
try {
if (source==target)
return true;
if (target instanceof Participant) {
Participant p = (Participant) target;
if (p.equals(modelHandler.getInternalParticipant())) {
return true;
}
if (p.getProcessRef() == null) {
return true;
}
if (p.getProcessRef().getLaneSets().isEmpty()) {
return true;
}
}
else if (target instanceof FlowElementsContainer) {
FlowElementsContainer p = (FlowElementsContainer) target;
if (p.getLaneSets().isEmpty()) {
return true;
}
}
} catch (Exception e) {
Activator.logError(e);
}
return false;
}
// TODO: I have no idea what this was supposed to do.
// This is preventing the move of a shape out of a FlowElementsContainer
// @Override
// public void move(FlowNode node, Object source, Object target) {
// try {
// if (ModelHandler.getInstance(node).getInternalParticipant() == null){ // this is not a collaboration, don't move
// return;
// }else{
// super.move(node, source, target);
// }
// } catch (IOException e) {
// Activator.logError(e);
// }
// }
}
/**
* The Class FromFlowElementsContainerAlgorithm.
*/
class FromFlowElementsContainerAlgorithm extends DefaultAlgorithm {
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#getType()
*/
@Override
public int getType() {
return TYPE_FROM;
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#canApplyTo(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
public boolean canApplyTo(IMoveShapeContext context) {
Object bo = getBusinessObjectForPictogramElement(context.getSourceContainer());
return bo != null && bo instanceof FlowElementsContainer;
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.DefaultAlgorithm#move(org.eclipse.bpmn2.FlowNode, java.lang.Object, java.lang.Object)
*/
@Override
public void move(FlowNode node, Object source, Object target) {
}
}
/**
* The Class ToFlowElementsContainerAlgorithm.
*/
class ToFlowElementsContainerAlgorithm extends DefaultAlgorithm {
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#getType()
*/
@Override
public int getType() {
return TYPE_TO;
}
/* (non-Javadoc)
* @see org.eclipse.bpmn2.modeler.core.features.MoveFlowNodeFeature.Algorithm#canApplyTo(org.eclipse.graphiti.features.context.IMoveShapeContext)
*/
@Override
public boolean canApplyTo(IMoveShapeContext context) {
Object bo = getBusinessObjectForPictogramElement(context.getTargetContainer());
return bo != null && bo instanceof FlowElementsContainer;
}
}
}