/**
*
*/
package org.activiti.designer.features;
import java.util.List;
import org.activiti.bpmn.model.Activity;
import org.activiti.bpmn.model.Artifact;
import org.activiti.bpmn.model.BaseElement;
import org.activiti.bpmn.model.BoundaryEvent;
import org.activiti.bpmn.model.CallActivity;
import org.activiti.bpmn.model.Event;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Gateway;
import org.activiti.bpmn.model.Lane;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.SubProcess;
import org.activiti.bpmn.model.Task;
import org.activiti.designer.util.eclipse.ActivitiUiUtil;
import org.activiti.designer.util.editor.Bpmn2MemoryModel;
import org.activiti.designer.util.editor.ModelHandler;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.ICreateContext;
import org.eclipse.graphiti.features.context.impl.CreateConnectionContext;
import org.eclipse.graphiti.features.context.impl.CreateContext;
import org.eclipse.graphiti.features.impl.AbstractCreateFeature;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.AnchorContainer;
import org.eclipse.graphiti.mm.pictograms.ChopboxAnchor;
import org.eclipse.graphiti.mm.pictograms.Connection;
import org.eclipse.graphiti.mm.pictograms.ContainerShape;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.services.Graphiti;
/**
* @author Tiese Barrell
* @version 2
* @since 0.5.0
*
*/
public abstract class AbstractCreateBPMNFeature extends AbstractCreateFeature {
private static final String CONNECTION_ATTRIBUTE = "org.activiti.designer.connectionContext";
public AbstractCreateBPMNFeature(IFeatureProvider fp, String name, String description) {
super(fp, name, description);
}
protected abstract String getFeatureIdKey();
protected String getNextId(BaseElement element) {
return ActivitiUiUtil.getNextId(element.getClass(), getFeatureIdKey(), getDiagram());
}
protected String getNextId(BaseElement element, String featureIdKey) {
return ActivitiUiUtil.getNextId(element.getClass(), featureIdKey, getDiagram());
}
public boolean canCreate(ICreateContext context) {
Object parentObject = getBusinessObjectForPictogramElement(context.getTargetContainer());
return (context.getTargetContainer() instanceof Diagram ||
parentObject instanceof SubProcess || parentObject instanceof Lane);
}
private void addFlowNodeOrArtifact(final BaseElement baseElement, final BaseElement container) {
if (container instanceof Process) {
final Process process = (Process) container;
if (baseElement instanceof FlowElement) {
process.addFlowElement((FlowElement) baseElement);
} else if (baseElement instanceof Artifact) {
process.addArtifact((Artifact) baseElement);
} else {
throw new IllegalArgumentException("BaseElement must be FlowElement or Artifact.");
}
} else if (container instanceof SubProcess) {
final SubProcess subProcess = (SubProcess) container;
if (baseElement instanceof FlowElement) {
subProcess.addFlowElement((FlowElement) baseElement);
} else if (baseElement instanceof Artifact) {
subProcess.addArtifact((Artifact) baseElement);
} else {
throw new IllegalArgumentException("BaseElement must be FlowElement or Artifact.");
}
} else {
throw new IllegalArgumentException("Container must be Process or SubProcess.");
}
}
/**
* Adds the given base element to the context. At first, a new ID is generated for the new object.
* Depending on the type of element, it is added as artifact or flow element.
*
* @param context the context to add it
* @param baseElement the base element to add
*/
protected void addObjectToContainer(ICreateContext context, BaseElement baseElement) {
baseElement.setId(getNextId(baseElement));
final ContainerShape targetContainer = context.getTargetContainer();
addBaseElementToContainer(targetContainer, baseElement);
addGraphicalContent(context, baseElement);
}
protected void addBaseElementToContainer(ContainerShape targetContainer, BaseElement baseElement) {
if (targetContainer instanceof Diagram) {
final Bpmn2MemoryModel model = ModelHandler.getModel(EcoreUtil.getURI(getDiagram()));
if (model.getBpmnModel().getMainProcess() == null) {
model.addMainProcess();
}
addFlowNodeOrArtifact(baseElement, model.getBpmnModel().getMainProcess());
}
else {
// find the parent object
final Object parent = getBusinessObjectForPictogramElement(targetContainer);
if (parent instanceof SubProcess) {
boolean addToSubProcess = true;
if (baseElement instanceof BoundaryEvent) {
BoundaryEvent boundaryEvent = (BoundaryEvent) baseElement;
if (boundaryEvent.getAttachedToRef() != null && boundaryEvent.getAttachedToRef().getId().equals(((SubProcess) parent).getId())) {
addToSubProcess = false;
}
}
if (addToSubProcess == false) {
ContainerShape parentContainer = targetContainer.getContainer();
addBaseElementToContainer(parentContainer, baseElement);
} else {
addFlowNodeOrArtifact(baseElement, (SubProcess) parent);
}
} else if (parent instanceof Lane) {
final Lane lane = (Lane) parent;
// for flow elements, the lane gets informed about the flow elements Id
if (baseElement instanceof FlowElement)
{
final FlowElement flowElement = (FlowElement) baseElement;
lane.getFlowReferences().add(flowElement.getId());
}
addFlowNodeOrArtifact(baseElement, lane.getParentProcess());
} else if (parent instanceof Activity) {
ContainerShape parentContainer = targetContainer.getContainer();
addBaseElementToContainer(parentContainer, baseElement);
}
}
}
protected void addObjectToContainer(ICreateContext context, FlowNode flowNode, String name) {
setName(name, flowNode, context);
addObjectToContainer(context, flowNode);
}
@SuppressWarnings("unchecked")
protected void addGraphicalContent(ICreateContext context, BaseElement targetElement) {
setLocation(targetElement, (CreateContext) context);
PictogramElement element = addGraphicalRepresentation(context, targetElement);
createConnectionIfNeeded(element, context);
Anchor elementAnchor = null;
EList<Anchor> anchorList = ((ContainerShape) element).getAnchors();
for (Anchor anchor : anchorList) {
if(anchor instanceof ChopboxAnchor) {
elementAnchor = anchor;
break;
}
}
if(context.getProperty("org.activiti.designer.changetype.sourceflows") != null) {
List<SequenceFlow> sourceFlows = (List<SequenceFlow>) context.getProperty("org.activiti.designer.changetype.sourceflows");
for (SequenceFlow sourceFlow : sourceFlows) {
sourceFlow.setSourceRef(targetElement.getId());
if (targetElement instanceof FlowNode) {
((FlowNode) targetElement).getOutgoingFlows().add(sourceFlow);
}
Connection connection = (Connection) getFeatureProvider().getPictogramElementForBusinessObject(sourceFlow);
connection.setStart(elementAnchor);
elementAnchor.getOutgoingConnections().add(connection);
}
List<SequenceFlow> targetFlows = (List<SequenceFlow>) context.getProperty("org.activiti.designer.changetype.targetflows");
for (SequenceFlow targetFlow : targetFlows) {
targetFlow.setTargetRef(targetElement.getId());
if (targetElement instanceof FlowNode) {
((FlowNode) targetElement).getIncomingFlows().add(targetFlow);
}
Connection connection = (Connection) getFeatureProvider().getPictogramElementForBusinessObject(targetFlow);
connection.setEnd(elementAnchor);
elementAnchor.getIncomingConnections().add(connection);
}
}
}
protected void setName(String defaultName, FlowElement targetElement, ICreateContext context) {
if(context.getProperty("org.activiti.designer.changetype.name") != null) {
targetElement.setName(context.getProperty("org.activiti.designer.changetype.name").toString());
} else {
targetElement.setName(defaultName);
}
}
private void setLocation(BaseElement targetElement, CreateContext context) {
if(context.getProperty(CONNECTION_ATTRIBUTE) != null) {
CreateConnectionContext connectionContext = (CreateConnectionContext)
context.getProperty(CONNECTION_ATTRIBUTE);
PictogramElement sourceElement = connectionContext.getSourcePictogramElement();
Object sourceObject = getBusinessObjectForPictogramElement(sourceElement);
if(sourceObject instanceof Event && (targetElement instanceof Task || targetElement instanceof CallActivity)) {
context.setLocation(sourceElement.getGraphicsAlgorithm().getX() + 80,
sourceElement.getGraphicsAlgorithm().getY() - 10);
} else if(sourceObject instanceof Event && targetElement instanceof Gateway) {
context.setLocation(sourceElement.getGraphicsAlgorithm().getX() + 80,
sourceElement.getGraphicsAlgorithm().getY() - 3);
} else if(sourceObject instanceof Gateway && targetElement instanceof Event) {
context.setLocation(sourceElement.getGraphicsAlgorithm().getX() + 85,
sourceElement.getGraphicsAlgorithm().getY() + 3);
} else if(sourceObject instanceof Gateway && (targetElement instanceof Task || targetElement instanceof CallActivity)) {
context.setLocation(sourceElement.getGraphicsAlgorithm().getX() + 85,
sourceElement.getGraphicsAlgorithm().getY() - 7);
} else if((sourceObject instanceof Task || sourceObject instanceof CallActivity) && targetElement instanceof Gateway) {
context.setLocation(sourceElement.getGraphicsAlgorithm().getX() + 160,
sourceElement.getGraphicsAlgorithm().getY() + 7);
} else if((sourceObject instanceof Task || sourceObject instanceof CallActivity) && targetElement instanceof Event) {
context.setLocation(sourceElement.getGraphicsAlgorithm().getX() + 160,
sourceElement.getGraphicsAlgorithm().getY() + 10);
} else if((sourceObject instanceof Task || sourceObject instanceof CallActivity) && (targetElement instanceof Task || targetElement instanceof CallActivity)) {
context.setLocation(sourceElement.getGraphicsAlgorithm().getX() + 160,
sourceElement.getGraphicsAlgorithm().getY());
}
}
}
private void createConnectionIfNeeded(PictogramElement element, ICreateContext context) {
if(context.getProperty(CONNECTION_ATTRIBUTE) != null) {
CreateConnectionContext connectionContext = (CreateConnectionContext)
context.getProperty(CONNECTION_ATTRIBUTE);
connectionContext.setTargetPictogramElement(element);
connectionContext.setTargetAnchor(Graphiti.getPeService().getChopboxAnchor((AnchorContainer) element));
CreateSequenceFlowFeature sequenceFeature = new CreateSequenceFlowFeature(getFeatureProvider());
sequenceFeature.create(connectionContext);
}
}
}