/* 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.dromara.workflow.flowable; import org.flowable.bpmn.model.Event; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; import org.flowable.image.ProcessDiagramGenerator; import java.awt.*; import java.awt.image.BufferedImage; import java.io.InputStream; import java.util.List; import java.util.*; /** * Class to generate an image based the diagram interchange information in a BPMN 2.0 process. * * @author Joram Barrez * @author Tijs Rademakers * @author Zheng Ji */ public class CustomDefaultProcessDiagramGenerator implements ProcessDiagramGenerator { protected Map, ActivityDrawInstruction> activityDrawInstructions = new HashMap<>(); protected Map, ArtifactDrawInstruction> artifactDrawInstructions = new HashMap<>(); public CustomDefaultProcessDiagramGenerator() { this(1.0); } // The instructions on how to draw a certain construct is // created statically and stored in a map for performance. public CustomDefaultProcessDiagramGenerator(final double scaleFactor) { // start event activityDrawInstructions.put(StartEvent.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); StartEvent startEvent = (StartEvent) flowNode; if (startEvent.getEventDefinitions() != null && !startEvent.getEventDefinitions().isEmpty()) { EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0); if (eventDefinition instanceof TimerEventDefinition) { processDiagramCanvas.drawTimerStartEvent(graphicInfo, scaleFactor); } else if (eventDefinition instanceof ErrorEventDefinition) { processDiagramCanvas.drawErrorStartEvent(graphicInfo, scaleFactor); } else if (eventDefinition instanceof EscalationEventDefinition) { processDiagramCanvas.drawEscalationStartEvent(graphicInfo, scaleFactor); } else if (eventDefinition instanceof ConditionalEventDefinition) { processDiagramCanvas.drawConditionalStartEvent(graphicInfo, scaleFactor); } else if (eventDefinition instanceof SignalEventDefinition) { processDiagramCanvas.drawSignalStartEvent(graphicInfo, scaleFactor); } else if (eventDefinition instanceof MessageEventDefinition) { processDiagramCanvas.drawMessageStartEvent(graphicInfo, scaleFactor); } else { processDiagramCanvas.drawNoneStartEvent(graphicInfo); } } else { List eventTypeElements = startEvent.getExtensionElements().get("eventType"); if (eventTypeElements != null && eventTypeElements.size() > 0) { processDiagramCanvas.drawEventRegistryStartEvent(graphicInfo, scaleFactor); } else { processDiagramCanvas.drawNoneStartEvent(graphicInfo); } } } }); // signal catch activityDrawInstructions.put(IntermediateCatchEvent.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent) flowNode; if (intermediateCatchEvent.getEventDefinitions() != null && !intermediateCatchEvent.getEventDefinitions().isEmpty()) { if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) { processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, true, scaleFactor); } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) { processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, true, scaleFactor); } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) { processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, true, scaleFactor); } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof ConditionalEventDefinition) { processDiagramCanvas.drawCatchingConditionalEvent(flowNode.getName(), graphicInfo, true, scaleFactor); } } } }); // signal throw activityDrawInstructions.put(ThrowEvent.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); ThrowEvent throwEvent = (ThrowEvent) flowNode; if (throwEvent.getEventDefinitions() != null && !throwEvent.getEventDefinitions().isEmpty()) { if (throwEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) { processDiagramCanvas.drawThrowingSignalEvent(graphicInfo, scaleFactor); } else if (throwEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) { processDiagramCanvas.drawThrowingEscalationEvent(graphicInfo, scaleFactor); } else if (throwEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) { processDiagramCanvas.drawThrowingCompensateEvent(graphicInfo, scaleFactor); } else { processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor); } } else { processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor); } } }); // end event activityDrawInstructions.put(EndEvent.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); EndEvent endEvent = (EndEvent) flowNode; if (endEvent.getEventDefinitions() != null && !endEvent.getEventDefinitions().isEmpty()) { if (endEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) { processDiagramCanvas.drawErrorEndEvent(flowNode.getName(), graphicInfo, scaleFactor); } else if (endEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) { processDiagramCanvas.drawEscalationEndEvent(flowNode.getName(), graphicInfo, scaleFactor); } else { processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor); } } else { processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor); } } }); // task activityDrawInstructions.put(Task.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // user task activityDrawInstructions.put(UserTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawUserTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // script task activityDrawInstructions.put(ScriptTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawScriptTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // service task activityDrawInstructions.put(ServiceTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); ServiceTask serviceTask = (ServiceTask) flowNode; if ("camel".equalsIgnoreCase(serviceTask.getType())) { processDiagramCanvas.drawCamelTask(serviceTask.getName(), graphicInfo, scaleFactor); }else if (ServiceTask.HTTP_TASK.equalsIgnoreCase(serviceTask.getType())) { processDiagramCanvas.drawHttpTask(serviceTask.getName(), graphicInfo, scaleFactor); } else if (ServiceTask.DMN_TASK.equalsIgnoreCase(serviceTask.getType())) { processDiagramCanvas.drawDMNTask(serviceTask.getName(), graphicInfo, scaleFactor); } else if (ServiceTask.SHELL_TASK.equalsIgnoreCase(serviceTask.getType())) { processDiagramCanvas.drawShellTask(serviceTask.getName(), graphicInfo, scaleFactor); } else { processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor); } } }); // http service task activityDrawInstructions.put(HttpServiceTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawHttpTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // receive task activityDrawInstructions.put(ReceiveTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawReceiveTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // send task activityDrawInstructions.put(SendTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawSendTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // manual task activityDrawInstructions.put(ManualTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawManualTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // send event service task activityDrawInstructions.put(SendEventServiceTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawSendEventServiceTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // external worker service task activityDrawInstructions.put(ExternalWorkerServiceTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); ServiceTask serviceTask = (ServiceTask) flowNode; processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor); } }); // case service task activityDrawInstructions.put(CaseServiceTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawCaseServiceTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // businessRuleTask task activityDrawInstructions.put(BusinessRuleTask.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawBusinessRuleTask(flowNode.getName(), graphicInfo, scaleFactor); } }); // exclusive gateway activityDrawInstructions.put(ExclusiveGateway.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawExclusiveGateway(graphicInfo, scaleFactor); } }); // inclusive gateway activityDrawInstructions.put(InclusiveGateway.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawInclusiveGateway(graphicInfo, scaleFactor); } }); // parallel gateway activityDrawInstructions.put(ParallelGateway.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawParallelGateway(graphicInfo, scaleFactor); } }); // event based gateway activityDrawInstructions.put(EventGateway.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawEventBasedGateway(graphicInfo, scaleFactor); } }); // Boundary timer activityDrawInstructions.put(BoundaryEvent.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); BoundaryEvent boundaryEvent = (BoundaryEvent) flowNode; if (boundaryEvent.getEventDefinitions() != null && !boundaryEvent.getEventDefinitions().isEmpty()) { EventDefinition eventDefinition = boundaryEvent.getEventDefinitions().get(0); if (eventDefinition instanceof TimerEventDefinition) { processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); } else if (eventDefinition instanceof ConditionalEventDefinition) { processDiagramCanvas.drawCatchingConditionalEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); } else if (eventDefinition instanceof ErrorEventDefinition) { processDiagramCanvas.drawCatchingErrorEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); } else if (eventDefinition instanceof EscalationEventDefinition) { processDiagramCanvas.drawCatchingEscalationEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); } else if (eventDefinition instanceof SignalEventDefinition) { processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); } else if (eventDefinition instanceof MessageEventDefinition) { processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); } else if (eventDefinition instanceof CompensateEventDefinition) { processDiagramCanvas.drawCatchingCompensateEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); } } else { List eventTypeElements = boundaryEvent.getExtensionElements().get("eventType"); if (eventTypeElements != null && eventTypeElements.size() > 0) { processDiagramCanvas.drawCatchingEventRegistryEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); } } } }); // subprocess activityDrawInstructions.put(SubProcess.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); } else { processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); } } }); // transaction activityDrawInstructions.put(Transaction.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); } else { processDiagramCanvas.drawExpandedTransaction(flowNode.getName(), graphicInfo, scaleFactor); } } }); // Event subprocess activityDrawInstructions.put(EventSubProcess.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor); } else { processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor); } } }); // Adhoc subprocess activityDrawInstructions.put(AdhocSubProcess.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); } else { processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); } } }); // call activity activityDrawInstructions.put(CallActivity.class, new ActivityDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); processDiagramCanvas.drawCollapsedCallActivity(flowNode.getName(), graphicInfo, scaleFactor); } }); // text annotation artifactDrawInstructions.put(TextAnnotation.class, new ArtifactDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(artifact.getId()); TextAnnotation textAnnotation = (TextAnnotation) artifact; processDiagramCanvas.drawTextAnnotation(textAnnotation.getText(), graphicInfo, scaleFactor); } }); // association artifactDrawInstructions.put(Association.class, new ArtifactDrawInstruction() { @Override public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) { Association association = (Association) artifact; String sourceRef = association.getSourceRef(); String targetRef = association.getTargetRef(); // source and target can be instance of FlowElement or Artifact BaseElement sourceElement = bpmnModel.getFlowElement(sourceRef); BaseElement targetElement = bpmnModel.getFlowElement(targetRef); if (sourceElement == null) { sourceElement = bpmnModel.getArtifact(sourceRef); } if (targetElement == null) { targetElement = bpmnModel.getArtifact(targetRef); } List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId()); graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); int[] xPoints = new int[graphicInfoList.size()]; int[] yPoints = new int[graphicInfoList.size()]; for (int i = 1; i < graphicInfoList.size(); i++) { GraphicInfo graphicInfo = graphicInfoList.get(i); GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); if (i == 1) { xPoints[0] = (int) previousGraphicInfo.getX(); yPoints[0] = (int) previousGraphicInfo.getY(); } xPoints[i] = (int) graphicInfo.getX(); yPoints[i] = (int) graphicInfo.getY(); } AssociationDirection associationDirection = association.getAssociationDirection(); processDiagramCanvas.drawAssociation(xPoints, yPoints, associationDirection, false, scaleFactor); } }); } @Override public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateImage(imageType); } @Override public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, boolean drawSequenceFlowNameWithNoLabelDI) { return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, 1.0, drawSequenceFlowNameWithNoLabelDI); } @Override public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI); } @Override public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, boolean drawSequenceFlowNameWithNoLabelDI) { return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI); } @Override public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI); } @Override public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, boolean drawSequenceFlowNameWithNoLabelDI) { return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(), activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0, drawSequenceFlowNameWithNoLabelDI); } @Override public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(), activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI); } @Override public InputStream generatePngDiagram(BpmnModel bpmnModel, boolean drawSequenceFlowNameWithNoLabelDI) { return generatePngDiagram(bpmnModel, 1.0, drawSequenceFlowNameWithNoLabelDI); } @Override public InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { return generateDiagram(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI); } @Override public InputStream generateJpgDiagram(BpmnModel bpmnModel) { return generateJpgDiagram(bpmnModel, 1.0, false); } @Override public InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { return generateDiagram(bpmnModel, "jpg", Collections.emptyList(), Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI); } public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateBufferedImage(imageType); } public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { return generateImage(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI); } @Override public BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor) { return generateImage(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, false); } protected CustomDefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { prepareBpmnModel(bpmnModel); CustomDefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); // Draw pool shape, if process is participant in collaboration for (Pool pool : bpmnModel.getPools()) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId()); processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo, scaleFactor); } // Draw lanes for (Process process : bpmnModel.getProcesses()) { for (Lane lane : process.getLanes()) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId()); processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo, scaleFactor); } } // Draw activities and their sequence-flows for (Process process : bpmnModel.getProcesses()) { for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) { if (!isPartOfCollapsedSubProcess(flowNode, bpmnModel)) { drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); } } } // Draw artifacts for (Process process : bpmnModel.getProcesses()) { for (Artifact artifact : process.getArtifacts()) { drawArtifact(processDiagramCanvas, bpmnModel, artifact); } List subProcesses = process.findFlowElementsOfType(SubProcess.class, true); if (subProcesses != null) { for (SubProcess subProcess : subProcesses) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(subProcess.getId()); if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { continue; } if (!isPartOfCollapsedSubProcess(subProcess, bpmnModel)) { for (Artifact subProcessArtifact : subProcess.getArtifacts()) { drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact); } } } } } return processDiagramCanvas; } protected void prepareBpmnModel(BpmnModel bpmnModel) { // Need to make sure all elements have positive x and y. // Check all graphicInfo and update the elements accordingly List allGraphicInfos = new ArrayList<>(); if (bpmnModel.getLocationMap() != null) { allGraphicInfos.addAll(bpmnModel.getLocationMap().values()); } if (bpmnModel.getLabelLocationMap() != null) { allGraphicInfos.addAll(bpmnModel.getLabelLocationMap().values()); } if (bpmnModel.getFlowLocationMap() != null) { for (List flowGraphicInfos : bpmnModel.getFlowLocationMap().values()) { allGraphicInfos.addAll(flowGraphicInfos); } } if (allGraphicInfos.size() > 0) { boolean needsTranslationX = false; boolean needsTranslationY = false; double lowestX = 0.0; double lowestY = 0.0; // Collect lowest x and y for (GraphicInfo graphicInfo : allGraphicInfos) { double x = graphicInfo.getX(); double y = graphicInfo.getY(); if (x < lowestX) { needsTranslationX = true; lowestX = x; } if (y < lowestY) { needsTranslationY = true; lowestY = y; } } // Update all graphicInfo objects if (needsTranslationX || needsTranslationY) { double translationX = Math.abs(lowestX); double translationY = Math.abs(lowestY); for (GraphicInfo graphicInfo : allGraphicInfos) { if (needsTranslationX) { graphicInfo.setX(graphicInfo.getX() + translationX); } if (needsTranslationY) { graphicInfo.setY(graphicInfo.getY() + translationY); } } } } } protected void drawActivity(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) { ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass()); if (drawInstruction != null) { drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode); // Gather info on the multi instance marker boolean multiInstanceSequential = false; boolean multiInstanceParallel = false; boolean collapsed = false; if (flowNode instanceof Activity) { Activity activity = (Activity) flowNode; MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics(); if (multiInstanceLoopCharacteristics != null) { multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential(); multiInstanceParallel = !multiInstanceSequential; } } // Gather info on the collapsed marker GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); if (flowNode instanceof SubProcess) { collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded(); } else if (flowNode instanceof CallActivity) { collapsed = true; } if (scaleFactor == 1.0) { // Actually draw the markers processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), multiInstanceSequential, multiInstanceParallel, collapsed); } // Draw highlighted activities if (highLightedActivities.contains(flowNode.getId())) { drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) { drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); } } else if (flowNode instanceof Task) { activityDrawInstructions.get(Task.class).draw(processDiagramCanvas, bpmnModel, flowNode); if (highLightedActivities.contains(flowNode.getId())) { drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) { drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); } } // Outgoing transitions of activity for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId())); String defaultFlow = null; if (flowNode instanceof Activity) { defaultFlow = ((Activity) flowNode).getDefaultFlow(); } else if (flowNode instanceof Gateway) { defaultFlow = ((Gateway) flowNode).getDefaultFlow(); } boolean isDefault = false; if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) { isDefault = true; } boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && sequenceFlow.getConditionExpression().trim().length() > 0 && !(flowNode instanceof Gateway); String sourceRef = sequenceFlow.getSourceRef(); String targetRef = sequenceFlow.getTargetRef(); FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef); FlowElement targetElement = bpmnModel.getFlowElement(targetRef); List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); if (graphicInfoList != null && graphicInfoList.size() > 0) { graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); int[] xPoints = new int[graphicInfoList.size()]; int[] yPoints = new int[graphicInfoList.size()]; for (int i = 1; i < graphicInfoList.size(); i++) { GraphicInfo graphicInfo = graphicInfoList.get(i); GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); if (i == 1) { xPoints[0] = (int) previousGraphicInfo.getX(); yPoints[0] = (int) previousGraphicInfo.getY(); } xPoints[i] = (int) graphicInfo.getX(); yPoints[i] = (int) graphicInfo.getY(); } processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor); // Draw sequenceflow label GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId()); if (labelGraphicInfo != null) { processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false); } else { if (drawSequenceFlowNameWithNoLabelDI) { GraphicInfo lineCenter = getLineCenter(graphicInfoList); processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false); } } } } // Nested elements if (flowNode instanceof FlowElementsContainer) { for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) { if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) { drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); } } } } /** * This method makes coordinates of connection flow better. * * @param processDiagramCanvas * @param bpmnModel * @param sourceElement * @param targetElement * @param graphicInfoList * @return */ protected static List connectionPerfectionizer(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, BaseElement sourceElement, BaseElement targetElement, List graphicInfoList) { GraphicInfo sourceGraphicInfo = bpmnModel.getGraphicInfo(sourceElement.getId()); GraphicInfo targetGraphicInfo = bpmnModel.getGraphicInfo(targetElement.getId()); CustomDefaultProcessDiagramCanvas.SHAPE_TYPE sourceShapeType = getShapeType(sourceElement); CustomDefaultProcessDiagramCanvas.SHAPE_TYPE targetShapeType = getShapeType(targetElement); return processDiagramCanvas.connectionPerfectionizer(sourceShapeType, targetShapeType, sourceGraphicInfo, targetGraphicInfo, graphicInfoList); } /** * This method returns shape type of base element.
* Each element can be presented as rectangle, rhombus, or ellipse. * * @param baseElement * @return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE */ protected static CustomDefaultProcessDiagramCanvas.SHAPE_TYPE getShapeType(BaseElement baseElement) { if (baseElement instanceof Task || baseElement instanceof Activity || baseElement instanceof TextAnnotation) { return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rectangle; } else if (baseElement instanceof Gateway) { return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rhombus; } else if (baseElement instanceof Event) { return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Ellipse; } else { // unknown source element, just do not correct coordinates } return null; } protected static GraphicInfo getLineCenter(List graphicInfoList) { GraphicInfo gi = new GraphicInfo(); int[] xPoints = new int[graphicInfoList.size()]; int[] yPoints = new int[graphicInfoList.size()]; double length = 0; double[] lengths = new double[graphicInfoList.size()]; lengths[0] = 0; double m; for (int i = 1; i < graphicInfoList.size(); i++) { GraphicInfo graphicInfo = graphicInfoList.get(i); GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); if (i == 1) { xPoints[0] = (int) previousGraphicInfo.getX(); yPoints[0] = (int) previousGraphicInfo.getY(); } xPoints[i] = (int) graphicInfo.getX(); yPoints[i] = (int) graphicInfo.getY(); length += Math.sqrt( Math.pow((int) graphicInfo.getX() - (int) previousGraphicInfo.getX(), 2) + Math.pow((int) graphicInfo.getY() - (int) previousGraphicInfo.getY(), 2)); lengths[i] = length; } m = length / 2; int p1 = 0; int p2 = 1; for (int i = 1; i < lengths.length; i++) { double len = lengths[i]; p1 = i - 1; p2 = i; if (len > m) { break; } } GraphicInfo graphicInfo1 = graphicInfoList.get(p1); GraphicInfo graphicInfo2 = graphicInfoList.get(p2); double AB = (int) graphicInfo2.getX() - (int) graphicInfo1.getX(); double OA = (int) graphicInfo2.getY() - (int) graphicInfo1.getY(); double OB = lengths[p2] - lengths[p1]; double ob = m - lengths[p1]; double ab = AB * ob / OB; double oa = OA * ob / OB; double mx = graphicInfo1.getX() + ab; double my = graphicInfo1.getY() + oa; gi.setX(mx); gi.setY(my); return gi; } protected void drawArtifact(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) { ArtifactDrawInstruction drawInstruction = artifactDrawInstructions.get(artifact.getClass()); if (drawInstruction != null) { drawInstruction.draw(processDiagramCanvas, bpmnModel, artifact); } } private static void drawHighLight(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); } private static void drawHighLightRed(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { processDiagramCanvas.drawHighLightRed((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); } protected static CustomDefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { // We need to calculate maximum values to know how big the image will be in its entirety double minX = Double.MAX_VALUE; double maxX = 0; double minY = Double.MAX_VALUE; double maxY = 0; for (Pool pool : bpmnModel.getPools()) { GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId()); minX = graphicInfo.getX(); maxX = graphicInfo.getX() + graphicInfo.getWidth(); minY = graphicInfo.getY(); maxY = graphicInfo.getY() + graphicInfo.getHeight(); } List flowNodes = gatherAllFlowNodes(bpmnModel); for (FlowNode flowNode : flowNodes) { GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); // width if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) { maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth(); } if (flowNodeGraphicInfo.getX() < minX) { minX = flowNodeGraphicInfo.getX(); } // height if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) { maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight(); } if (flowNodeGraphicInfo.getY() < minY) { minY = flowNodeGraphicInfo.getY(); } for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); if (graphicInfoList != null) { for (GraphicInfo graphicInfo : graphicInfoList) { // width if (graphicInfo.getX() > maxX) { maxX = graphicInfo.getX(); } if (graphicInfo.getX() < minX) { minX = graphicInfo.getX(); } // height if (graphicInfo.getY() > maxY) { maxY = graphicInfo.getY(); } if (graphicInfo.getY() < minY) { minY = graphicInfo.getY(); } } } } } List artifacts = gatherAllArtifacts(bpmnModel); for (Artifact artifact : artifacts) { GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId()); if (artifactGraphicInfo != null) { // width if (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) { maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth(); } if (artifactGraphicInfo.getX() < minX) { minX = artifactGraphicInfo.getX(); } // height if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) { maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight(); } if (artifactGraphicInfo.getY() < minY) { minY = artifactGraphicInfo.getY(); } } List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId()); if (graphicInfoList != null) { for (GraphicInfo graphicInfo : graphicInfoList) { // width if (graphicInfo.getX() > maxX) { maxX = graphicInfo.getX(); } if (graphicInfo.getX() < minX) { minX = graphicInfo.getX(); } // height if (graphicInfo.getY() > maxY) { maxY = graphicInfo.getY(); } if (graphicInfo.getY() < minY) { minY = graphicInfo.getY(); } } } } int nrOfLanes = 0; for (Process process : bpmnModel.getProcesses()) { for (Lane l : process.getLanes()) { nrOfLanes++; GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId()); // // width if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) { maxX = graphicInfo.getX() + graphicInfo.getWidth(); } if (graphicInfo.getX() < minX) { minX = graphicInfo.getX(); } // height if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) { maxY = graphicInfo.getY() + graphicInfo.getHeight(); } if (graphicInfo.getY() < minY) { minY = graphicInfo.getY(); } } } // Special case, see https://activiti.atlassian.net/browse/ACT-1431 if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) { // Nothing to show minX = 0; minY = 0; } return new CustomDefaultProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); } protected static List gatherAllArtifacts(BpmnModel bpmnModel) { List artifacts = new ArrayList<>(); for (Process process : bpmnModel.getProcesses()) { artifacts.addAll(process.getArtifacts()); } return artifacts; } protected static List gatherAllFlowNodes(BpmnModel bpmnModel) { List flowNodes = new ArrayList<>(); for (Process process : bpmnModel.getProcesses()) { flowNodes.addAll(gatherAllFlowNodes(process)); } return flowNodes; } protected static List gatherAllFlowNodes(FlowElementsContainer flowElementsContainer) { List flowNodes = new ArrayList<>(); for (FlowElement flowElement : flowElementsContainer.getFlowElements()) { if (flowElement instanceof FlowNode) { flowNodes.add((FlowNode) flowElement); } if (flowElement instanceof FlowElementsContainer) { flowNodes.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement)); } } return flowNodes; } protected boolean isPartOfCollapsedSubProcess(FlowElement flowElement, BpmnModel model) { SubProcess subProcess = flowElement.getSubProcess(); if (subProcess != null) { GraphicInfo graphicInfo = model.getGraphicInfo(subProcess.getId()); if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { return true; } return isPartOfCollapsedSubProcess(subProcess, model); } return false; } public Map, ActivityDrawInstruction> getActivityDrawInstructions() { return activityDrawInstructions; } public void setActivityDrawInstructions( Map, ActivityDrawInstruction> activityDrawInstructions) { this.activityDrawInstructions = activityDrawInstructions; } public Map, ArtifactDrawInstruction> getArtifactDrawInstructions() { return artifactDrawInstructions; } public void setArtifactDrawInstructions( Map, ArtifactDrawInstruction> artifactDrawInstructions) { this.artifactDrawInstructions = artifactDrawInstructions; } protected interface ActivityDrawInstruction { void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode); } protected interface ArtifactDrawInstruction { void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact); } }