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/

欢迎阅读,各位老铁,如果对你有帮助,点个赞加个关注呗!~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执于代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值