org.activiti.bpmn
Survive by day and develop by night.
talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive.
happy for hardess to solve denpendies.
目录
概述
org.activiti.bpmn是一个非常常见的需求。
需求:
设计思路
实现思路分析
1.BpmnAutoLayout
private static final String STYLE_EVENT = "styleEvent";
private static final String STYLE_GATEWAY = "styleGateway";
private static final String STYLE_SEQUENCEFLOW = "styleSequenceFlow";
private static final String STYLE_BOUNDARY_SEQUENCEFLOW = "styleBoundarySequenceFlow";
protected BpmnModel bpmnModel;
protected int eventSize = 30;
protected int gatewaySize = 40;
protected int taskWidth = 100;
protected int taskHeight = 60;
protected int subProcessMargin = 20;
protected mxGraph graph;
protected Object cellParent;
protected Map<String, Association> associations;
protected Map<String, TextAnnotation> textAnnotations;
protected Map<String, SequenceFlow> sequenceFlows;
protected List<BoundaryEvent> boundaryEvents;
protected Map<String, FlowElement> handledFlowElements;
protected Map<String, Artifact> handledArtifacts;
protected Map<String, Object> generatedVertices;
protected Map<String, Object> generatedSequenceFlowEdges;
protected Map<String, Object> generatedAssociationEdges;
public BpmnAutoLayout(BpmnModel bpmnModel) {
this.bpmnModel = bpmnModel;
}
public void execute() {
// Reset any previous DI information
bpmnModel.getLocationMap().clear();
bpmnModel.getFlowLocationMap().clear();
// Generate DI for each process
for (Process process : bpmnModel.getProcesses()) {
layout(process);
// Operations that can only be done after all elements have received
// DI
translateNestedSubprocesses(process);
}
}
protected void layout(FlowElementsContainer flowElementsContainer) {
graph = new mxGraph();
cellParent = graph.getDefaultParent();
graph.getModel().beginUpdate();
// Subprocesses are handled in a new instance of BpmnAutoLayout, hence they instantiations of new maps here.
handledFlowElements = new HashMap<String, FlowElement>();
handledArtifacts = new HashMap<String, Artifact>();
generatedVertices = new HashMap<String, Object>();
generatedSequenceFlowEdges = new HashMap<String, Object>();
generatedAssociationEdges = new HashMap<String, Object>();
associations = new HashMap<String, Association>(); //Associations are gathered and processed afterwards, because we must be sure we already found source and target
textAnnotations = new HashMap<String, TextAnnotation>(); // Text Annotations are gathered and processed afterwards, because we must be sure we already found the parent.
sequenceFlows = new HashMap<String, SequenceFlow>(); // Sequence flow are gathered and processed afterwards,because we mustbe sure we already found source and target
boundaryEvents = new ArrayList<BoundaryEvent>(); // Boundary events are gathered and processed afterwards, because we must be sure we have its parent
// Process all elements
for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
if (flowElement instanceof SequenceFlow) {
handleSequenceFlow((SequenceFlow) flowElement);
} else if (flowElement instanceof Event) {
handleEvent(flowElement);
} else if (flowElement instanceof Gateway) {
createGatewayVertex(flowElement);
} else if (flowElement instanceof Task || flowElement instanceof CallActivity) {
handleActivity(flowElement);
} else if (flowElement instanceof SubProcess) {
handleSubProcess(flowElement);
}
handledFlowElements.put(flowElement.getId(), flowElement);
}
// process artifacts
for (Artifact artifact : flowElementsContainer.getArtifacts()) {
if (artifact instanceof Association) {
handleAssociation((Association) artifact);
} else if (artifact instanceof TextAnnotation) {
handleTextAnnotation((TextAnnotation) artifact);
}
handledArtifacts.put(artifact.getId(), artifact);
}
// Process gathered elements
handleBoundaryEvents();
handleSequenceFlow();
handleAssociations();
// All elements are now put in the graph. Let's layout them!
CustomLayout layout = new CustomLayout(graph, SwingConstants.WEST);
layout.setIntraCellSpacing(100.0);
layout.setResizeParent(true);
layout.setFineTuning(true);
layout.setParentBorder(20);
layout.setMoveParent(true);
layout.setDisableEdgeStyle(false);
layout.setUseBoundingBox(true);
layout.execute(graph.getDefaultParent());
graph.getModel().endUpdate();
generateDiagramInterchangeElements();
}
private void handleTextAnnotation(TextAnnotation artifact) {
ensureArtifactIdSet(artifact);
textAnnotations.put(artifact.getId(), artifact);
}
// BPMN element handling
protected void ensureSequenceFlowIdSet(SequenceFlow sequenceFlow) {
// We really must have ids for sequence flow to be able to generate
// stuff
if (sequenceFlow.getId() == null) {
sequenceFlow.setId("sequenceFlow-" + UUID.randomUUID().toString());
}
}
protected void ensureArtifactIdSet(Artifact artifact) {
// We really must have ids for sequence flow to be able to generate stuff
if (artifact.getId() == null) {
artifact.setId("artifact-" + UUID.randomUUID().toString());
}
}
protected void handleAssociation(Association association) {
ensureArtifactIdSet(association);
associations.put(association.getId(), association);
}
protected void handleSequenceFlow(SequenceFlow sequenceFlow) {
ensureSequenceFlowIdSet(sequenceFlow);
sequenceFlows.put(sequenceFlow.getId(), sequenceFlow);
}
protected void handleEvent(FlowElement flowElement) {
// Boundary events are an exception to the general way of drawing an
// event
if (flowElement instanceof BoundaryEvent) {
boundaryEvents.add((BoundaryEvent) flowElement);
} else {
createEventVertex(flowElement);
}
}
protected void handleActivity(FlowElement flowElement) {
Object activityVertex = graph.insertVertex(cellParent, flowElement.getId(), "", 0, 0, taskWidth, taskHeight);
generatedVertices.put(flowElement.getId(), activityVertex);
}
protected void handleSubProcess(FlowElement flowElement) {
BpmnAutoLayout bpmnAutoLayout = new BpmnAutoLayout(bpmnModel);
bpmnAutoLayout.layout((SubProcess) flowElement);
2.BPMNLayout
public class BPMNLayout extends mxGraphLayout {
// NEW
protected BpmnAutoLayout bpmnAutoLayout;
public void setBpmnAutoLayout(BpmnAutoLayout bpmnAutoLayout) {
this.bpmnAutoLayout = bpmnAutoLayout;
}
// NEW
/**
* Specifies the orientation of the layout. Default is true.
*/
protected boolean horizontal;
/**
* Specifies if edge directions should be inverted. Default is false.
*/
protected boolean invert;
/**
* If the parent should be resized to match the width/height of the tree. Default is true.
*/
protected boolean resizeParent = true;
/**
* Specifies if the tree should be moved to the top, left corner if it is inside a top-level layer. Default is true.
*/
protected boolean moveTree = true;
/**
* Specifies if all edge points of traversed edges should be removed. Default is true.
*/
protected boolean resetEdges = true;
/**
* Holds the levelDistance. Default is 40.
*/
protected int levelDistance = 40;
/**
* Holds the nodeDistance. Default is 20.
*/
protected int nodeDistance = 20;
/**
*
* @param graph
*/
public BPMNLayout(mxGraph graph) {
this(graph, true);
}
/**
*
* @param graph
* @param horizontal
*/
public BPMNLayout(mxGraph graph, boolean horizontal) {
this(graph, horizontal, false);
}
/**
*
* @param graph
* @param horizontal
* @param invert
*/
public BPMNLayout(mxGraph graph, boolean horizontal, boolean invert) {
super(graph);
setUseBoundingBox(false);
this.horizontal = horizontal;
this.invert = invert;
}
public mxGraph getGraph() {
return (mxGraph) graph;
}
/**
* Returns a boolean indicating if the given <em>mxCell</em> should be ignored as a vertex. This returns true if the cell has no connections.
*
* @param vertex
* Object that represents the vertex to be tested.
* @return Returns true if the vertex should be ignored.
*/
public boolean isVertexIgnored(Object vertex) {
return super.isVertexIgnored(vertex) || graph.isSwimlane(vertex) || graph.getModel().getGeometry(vertex).isRelative() || graph.getConnections(vertex).length == 0;
}
/**
* @return the horizontal
*/
public boolean isHorizontal() {
return horizontal;
}
/**
* @param horizontal
* the horizontal to set
*/
public void setHorizontal(boolean horizontal) {
this.horizontal = horizontal;
}
/**
* @return the invert
*/
public boolean isInvert() {
return invert;
}
/**
* @param invert
* the invert to set
*/
public void setInvert(boolean invert) {
this.invert = invert;
}
/**
* @return the resizeParent
*/
public boolean isResizeParent() {
return resizeParent;
}
/**
* @param resizeParent
* the resizeParent to set
*/
public void setResizeParent(boolean resizeParent) {
this.resizeParent = resizeParent;
}
/**
* @return the moveTree
*/
public boolean isMoveTree() {
return moveTree;
}
/**
* @param moveTree
* the moveTree to set
*/
public void setMoveTree(boolean moveTree) {
this.moveTree = moveTree;
}
/**
* @return the resetEdges
*/
public boolean isResetEdges() {
return resetEdges;
}
/**
* @param resetEdges
* the resetEdges to set
*/
public void setResetEdges(boolean resetEdges) {
this.resetEdges = resetEdges;
}
/**
* @return the levelDistance
*/
public int getLevelDistance() {
return levelDistance;
}
/**
* @param levelDistance
* the levelDistance to set
*/
public void setLevelDistance(int levelDistance) {
this.levelDistance = levelDistance;
}
/**
* @return the nodeDistance
*/
public int getNodeDistance() {
return nodeDistance;
}
/**
* @param nodeDistance
* the nodeDistance to set
*/
public void setNodeDistance(int nodeDistance) {
this.nodeDistance = nodeDistance;
}
public void execute(Object parent) {
mxIGraphModel model = graph.getModel();
List<Object> roots = graph.findTreeRoots(parent, true, invert);
// if (getGraph().isOrganizationElement(parent)) {
// roots = asList(graph.getSelectionCells());
// }
for (Object root : roots) {
parent = model.getParent(root);
if (isBoundaryEvent(root)) {
parent = model.getParent(parent);
}
model.beginUpdate();
try {
TreeNode node = dfs(root, parent, null);
if (node != null) {
layout(node);
double x0 = graph.getGridSize();
double y0 = x0;
if (!moveTree || parent == graph.getDefaultParent() || parent == graph.getCurrentRoot()) {
mxGeometry g = model.getGeometry(root);
if (g.isRelative()) {
g = model.getGeometry(model.getParent(root));
}
if (g != null) {
x0 = g.getX();
y0 = g.getY();
}
}
mxRectangle bounds = null;
if (horizontal) {
bounds = horizontalLayout(node, x0, y0, null);
} else {
bounds = verticalLayout(node, null, x0, y0, null);
}
if (bounds != null) {
double dx = 0;
double dy = 0;
if (bounds.getX() < 0) {
dx = Math.abs(x0 - bounds.getX());
}
if (bounds.getY() < 0) {
dy = Math.abs(y0 - bounds.getY());
}
if (parent != null) {
mxRectangle size = graph.getStartSize(parent);
dx += size.getWidth();
dy += size.getHeight();
// Resize parent swimlane
if (resizeParent && !graph.isCellCollapsed(parent)) {
mxGeometry g = model.getGeometry(parent);
if (g != null) {
double width = bounds.getWidth() + size.getWidth() - bounds.getX() + 2 * x0;
double height = bounds.getHeight() + size.getHeight() - bounds.getY() + 2 * y0;
g = (mxGeometry) g.clone();
if (g.getWidth() > width) {
dx += (g.getWidth() - width) / 2;
} else {
g.setWidth(width);
}
if (g.getHeight() > height) {
if (horizontal) {
dy += (g.getHeight() - height) / 2;
}
} else {
g.setHeight(height);
}
model.setGeometry(parent, g);
}
}
}
if (model.getParent(node.cell) != graph.getCurrentRoot() && model.getParent(node.cell) != graph.getDefaultParent()) {
moveNode(node, dx, dy);
}
}
}
} finally {
model.endUpdate();
}
}
}
protected boolean isBoundaryEvent(Object obj) {
if (obj instanceof mxCell) {
mxCell cell = (mxCell) obj;
return cell.getId().startsWith("boundary-event-");
}
return false;
}
/**
* Moves the specified node and all of its children by the given amount.
*/
protected void moveNode(TreeNode node, double dx, double dy) {
node.x += dx;
node.y += dy;
apply(node, null);
TreeNode child = node.child;
while (child != null) {
moveNode(child, dx, dy);
child = child.next;
}
}
/**
* Does a depth first search starting at the specified cell. Makes sure the specified swimlane is never left by the algorithm.
*/
protected TreeNode dfs(Object cell, Object parent, Set<Object> visited) {
if (visited == null) {
visited = new HashSet<Object>();
}
TreeNode node = null;
参考资料和推荐阅读
[1]. https://www.activiti.org/
欢迎阅读,各位老铁,如果对你有帮助,点个赞加个关注呗!~