/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.activiti.engine.impl.bpmn.diagram;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.impl.bpmn.behavior.BoundaryEventActivityBehavior;
import org.activiti.engine.impl.bpmn.behavior.CallActivityBehavior;
import org.activiti.engine.impl.bpmn.parser.BpmnParse;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.Lane;
import org.activiti.engine.impl.pvm.process.LaneSet;
import org.activiti.engine.impl.pvm.process.ParticipantProcess;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
/**
* Class to generate an image based the diagram interchange information in a
* BPMN 2.0 process.
*
* @author Joram Barrez
*/
public class ProcessDiagramGenerator {
protected static final Map<String, ActivityDrawInstruction> activityDrawInstructions = new HashMap<String, ActivityDrawInstruction>();
// The instructions on how to draw a certain construct is
// created statically and stored in a map for performance.
static {
// start event
activityDrawInstructions.put("startEvent", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawNoneStartEvent(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// start timer event
activityDrawInstructions.put("startTimerEvent", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawTimerStartEvent(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// signal catch
activityDrawInstructions.put("intermediateSignalCatch", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawCatchingSignalEvent((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight(), false);
}
});
// signal throw
activityDrawInstructions.put("intermediateSignalThrow", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawThrowingSignalEvent(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// none throw
activityDrawInstructions.put("intermediateThrowEvent", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawThrowingNoneEvent(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// end event
activityDrawInstructions.put("endEvent", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawNoneEndEvent(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// error end event
activityDrawInstructions.put("errorEndEvent", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawErrorEndEvent((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// error start event
activityDrawInstructions.put("errorStartEvent", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawErrorStartEvent(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// task
activityDrawInstructions.put("task", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
// user task
activityDrawInstructions.put("userTask", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawUserTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
// script task
activityDrawInstructions.put("scriptTask", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawScriptTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
// service task
activityDrawInstructions.put("serviceTask", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawServiceTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
// receive task
activityDrawInstructions.put("receiveTask", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawReceiveTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
// send task
activityDrawInstructions.put("sendTask", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawSendTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
// manual task
activityDrawInstructions.put("manualTask", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawManualTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
// businessRuleTask task
activityDrawInstructions.put("businessRuleTask", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawBusinessRuleTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
// exclusive gateway
activityDrawInstructions.put("exclusiveGateway", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawExclusiveGateway(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// inclusive gateway
activityDrawInstructions.put("inclusiveGateway", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawInclusiveGateway(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// parallel gateway
activityDrawInstructions.put("parallelGateway", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawParallelGateway(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// event based gateway
activityDrawInstructions.put("eventBasedGateway", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawEventBasedGateway(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight());
}
});
// Boundary timer
activityDrawInstructions.put("boundaryTimer", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
BoundaryEventActivityBehavior behavior = (BoundaryEventActivityBehavior)activityImpl.getActivityBehavior();
processDiagramCreator.drawCatchingTimerEvent((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight(), behavior.isInterrupting());
}
});
// Boundary catch error
activityDrawInstructions.put("boundaryError", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
BoundaryEventActivityBehavior behavior = (BoundaryEventActivityBehavior)activityImpl.getActivityBehavior();
processDiagramCreator.drawCatchingErrorEvent(activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight(), behavior.isInterrupting());
}
});
// Boundary signal event
activityDrawInstructions.put("boundarySignal", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
BoundaryEventActivityBehavior behavior = (BoundaryEventActivityBehavior)activityImpl.getActivityBehavior();
processDiagramCreator.drawCatchingSignalEvent((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight(), behavior.isInterrupting());
}
});
// timer catch event
activityDrawInstructions.put("intermediateTimer", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawCatchingTimerEvent((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(), activityImpl.getHeight(), false);
}
});
// subprocess
activityDrawInstructions.put("subProcess", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
Boolean isExpanded = (Boolean) activityImpl.getProperty(BpmnParse.PROPERTYNAME_ISEXPANDED);
Boolean isTriggeredByEvent = (Boolean) activityImpl.getProperty("triggeredByEvent");
if(isTriggeredByEvent == null) {
isTriggeredByEvent = Boolean.TRUE;
}
if (isExpanded != null && isExpanded == false) {
processDiagramCreator.drawCollapsedSubProcess((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(),
activityImpl.getWidth(), activityImpl.getHeight(), isTriggeredByEvent);
} else {
processDiagramCreator.drawExpandedSubProcess((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(),
activityImpl.getWidth(), activityImpl.getHeight(), isTriggeredByEvent);
}
}
});
// call activity
activityDrawInstructions.put("callActivity", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawCollapsedCallActivity((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(),
activityImpl.getWidth(), activityImpl.getHeight());
}
});
// jenkins task
activityDrawInstructions.put("jenkinsTask", new ActivityDrawInstruction() {
public void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl) {
processDiagramCreator.drawScriptTask((String) activityImpl.getProperty("name"), activityImpl.getX(), activityImpl.getY(), activityImpl.getWidth(),
activityImpl.getHeight());
}
});
}
/**
* Generates a PNG diagram image of the given process definition, using the
* diagram interchange information of the process.
*/
public static InputStream generatePngDiagram(ProcessDefinitionEntity processDefinition) {
return generateDiagram(processDefinition, "png", Collections.<String> emptyList());
}
/**
* Generates a JPG diagram image of the given process definition, using the
* diagram interchange information of the process.
*/
public static InputStream generateJpgDiagram(ProcessDefinitionEntity processDefinition) {
return generateDiagram(processDefinition, "jpg", Collections.<String> emptyList());
}
protected static ProcessDiagramCanvas generateDiagram(ProcessDefinitionEntity processDefinition, List<String> highLightedActivities) {
return generateDiagram(processDefinition, highLightedActivities, Collections.<String> emptyList());
}
protected static ProcessDiagramCanvas generateDiagram(ProcessDefinitionEntity processDefinition, List<String> highLightedActivities, List<String> highLightedFlows) {
ProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(processDefinition);
// Draw pool shape, if process is participant in collaboration
if(processDefinition.getParticipantProcess() != null) {
ParticipantProcess pProc = processDefinition.getParticipantProcess();
processDiagramCanvas.drawPoolOrLane(pProc.getName(), pProc.getX(), pProc.getY(), pProc.getWidth(), pProc.getHeight());
}
// Draw lanes
if(processDefinition.getLaneSets() != null && processDefinition.getLaneSets().size() > 0) {
for(LaneSet laneSet : processDefinition.getLaneSets()) {
if(laneSet.getLanes() != null && laneSet.getLanes().size() > 0) {
for(Lane lane : laneSet.getLanes()) {
processDiagramCanvas.drawPoolOrLane(lane.getName(), lane.getX(), lane.getY(), lane.getWidth(), lane.getHeight());
}
}
}
}
// Draw activities and their sequence-flows
for (ActivityImpl activity : processDefinition.getActivities()) {
drawActivity(processDiagramCanvas, activity, highLightedActivities, highLightedFlows);
}
return processDiagramCanvas;
}
public static InputStream generateDiagram(ProcessDefinitionEntity processDefinition, String imageType, List<String> highLightedActivities) {
return generateDiagram(processDefinition, highLightedActivities, Collections.<String> emptyList()).generateImage(imageType);
}
public static InputStream generateDiagram(ProcessDefinitionEntity processDefinition, String imageType, List<String> highLightedActivities, List<String> highLightedFlows) {
return generateDiagram(processDefinition, highLightedActivities, highLightedFlows).generateImage(imageType);
}
protected static void drawActivity(ProcessDiagramCanvas processDiagramCanvas, ActivityImpl activity, List<String> highLightedActivities, List<String> highLightedFlows) {
String type = (String) activity.getProperty("type");
ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(type);
if (drawInstruction != null) {
drawInstruction.draw(processDiagramCanvas, activity);
// Gather info on the multi instance marker
boolean multiInstanceSequential = false, multiInstanceParallel = false, collapsed = false;
String multiInstance = (String) activity.getProperty("multiInstance");
if (multiInstance != null) {
if ("sequential".equals(multiInstance)) {
multiInstanceSequential = true;
} else {
multiInstanceParallel = true;
}
}
// Gather info on the collapsed marker
collapsed = (activity.getActivityBehavior() instanceof CallActivityBehavior);
Boolean expanded = (Boolean) activity.getProperty(BpmnParse.PROPERTYNAME_ISEXPANDED);
if (expanded != null) {
collapsed = !expanded;
}
// Actually draw the markers
processDiagramCanvas.drawActivityMarkers(activity.getX(), activity.getY(), activity.getWidth(), activity.getHeight(), multiInstanceSequential,
multiInstanceParallel, collapsed);
// Draw highlighted activities
if (highLightedActivities.contains(activity.getId())) {
drawHighLight(processDiagramCanvas, activity);
}
}
/*
// Original transitions drawing
// Outgoing transitions of activity
for (PvmTransition sequenceFlow : activity.getOutgoingTransitions()) {
List<Integer> waypoints = ((TransitionImpl) sequenceFlow).getWaypoints();
for (int i = 2; i < waypoints.size(); i += 2) { // waypoints.size()
// minimally 4: x1, y1,
// x2, y2
boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
boolean drawConditionalIndicator = (i == 2) && sequenceFlow.getProperty(BpmnParse.PROPERTYNAME_CONDITION) != null
&& !((String) activity.getProperty("type")).toLowerCase().contains("gateway");
if (i < waypoints.size() - 2) {
processDiagramCanvas.drawSequenceflowWithoutArrow(waypoints.get(i - 2), waypoints.get(i - 1), waypoints.get(i), waypoints.get(i + 1),
drawConditionalIndicator, highLighted);
} else {
processDiagramCanvas.drawSequenceflow(waypoints.get(i - 2), waypoints.get(i - 1), waypoints.get(i), waypoints.get(i + 1), drawConditionalIndicator, highLighted);
}
}
}*/
// Outgoing transitions of activity
for (PvmTransition sequenceFlow : activity.getOutgoingTransitions()) {
boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
boolean drawConditionalIndicator = sequenceFlow.getProperty(BpmnParse.PROPERTYNAME_CONDITION) != null
&& !((String) activity.getProperty("type")).toLowerCase().contains("gateway");
boolean isDefault = sequenceFlow.getId().equals(activity.getProperty("default"))
&& ((String) activity.getProperty("type")).toLowerCase().contains("gateway");
List<Integer> waypoints = ((TransitionImpl) sequenceFlow).getWaypoints();
int xPoints[]= new int[waypoints.size()/2];
int yPoints[]= new int[waypoints.size()/2];
for (int i=0, j=0; i < waypoints.size(); i+=2, j++) { // waypoints.size()
// minimally 4: x1, y1,
// x2, y2
xPoints[j] = waypoints.get(i);
yPoints[j] = waypoints.get(i+1);
}
processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted);
}
// Nested activities (boundary events)
for (ActivityImpl nestedActivity : activity.getActivities()) {
drawActivity(processDiagramCanvas, nestedActivity, highLightedActivities, highLightedFlows);
}
}
private static void drawHighLight(ProcessDiagramCanvas processDiagramCanvas, ActivityImpl activity) {
processDiagramCanvas.drawHighLight(activity.getX(), activity.getY(), activity.getWidth(), activity.getHeight());
}
protected static ProcessDiagramCanvas initProcessDiagramCanvas(ProcessDefinitionEntity processDefinition) {
int minX = Integer.MAX_VALUE;
int maxX = 0;
int minY = Integer.MAX_VALUE;
int maxY = 0;
if(processDefinition.getParticipantProcess() != null) {
ParticipantProcess pProc = processDefinition.getParticipantProcess();
minX = pProc.getX();
maxX = pProc.getX() + pProc.getWidth();
minY = pProc.getY();
maxY = pProc.getY() + pProc.getHeight();
}
for (ActivityImpl activity : processDefinition.getActivities()) {
// width
if (activity.getX() + activity.getWidth() > maxX) {
maxX = activity.getX() + activity.getWidth();
}
if (activity.getX() < minX) {
minX = activity.getX();
}
// height
if (activity.getY() + activity.getHeight() > maxY) {
maxY = activity.getY() + activity.getHeight();
}
if (activity.getY() < minY) {
minY = activity.getY();
}
for (PvmTransition sequenceFlow : activity.getOutgoingTransitions()) {
List<Integer> waypoints = ((TransitionImpl) sequenceFlow).getWaypoints();
for (int i = 0; i < waypoints.size(); i += 2) {
// width
if (waypoints.get(i) > maxX) {
maxX = waypoints.get(i);
}
if (waypoints.get(i) < minX) {
minX = waypoints.get(i);
}
// height
if (waypoints.get(i + 1) > maxY) {
maxY = waypoints.get(i + 1);
}
if (waypoints.get(i + 1) < minY) {
minY = waypoints.get(i + 1);
}
}
}
}
if(processDefinition.getLaneSets() != null && processDefinition.getLaneSets().size() > 0) {
for(LaneSet laneSet : processDefinition.getLaneSets()) {
if(laneSet.getLanes() != null && laneSet.getLanes().size() > 0) {
for(Lane lane : laneSet.getLanes()) {
// width
if (lane.getX() + lane.getWidth() > maxX) {
maxX = lane.getX() + lane.getWidth();
}
if (lane.getX() < minX) {
minX = lane.getX();
}
// height
if (lane.getY() + lane.getHeight() > maxY) {
maxY = lane.getY() + lane.getHeight();
}
if (lane.getY() < minY) {
minY = lane.getY();
}
}
}
}
}
// Special case, see http://jira.codehaus.org/browse/ACT-1431
if ( (processDefinition.getActivities() == null || processDefinition.getActivities().size() == 0)
&& (processDefinition.getLaneSets() == null || processDefinition.getLaneSets().size() == 0)) {
// Nothing to show
minX = 0;
minY = 0;
}
return new ProcessDiagramCanvas(maxX + 10, maxY + 10, minX, minY);
}
protected interface ActivityDrawInstruction {
void draw(ProcessDiagramCanvas processDiagramCreator, ActivityImpl activityImpl);
}
}