All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.zeebe.model.bpmn.builder.AbstractFlowNodeBuilder Maven / Gradle / Ivy

/*
 * Copyright © 2017 camunda services GmbH ([email protected])
 *
 * 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 io.zeebe.model.bpmn.builder;

import io.zeebe.model.bpmn.AssociationDirection;
import io.zeebe.model.bpmn.BpmnModelException;
import io.zeebe.model.bpmn.BpmnModelInstance;
import io.zeebe.model.bpmn.instance.Activity;
import io.zeebe.model.bpmn.instance.Association;
import io.zeebe.model.bpmn.instance.BoundaryEvent;
import io.zeebe.model.bpmn.instance.BusinessRuleTask;
import io.zeebe.model.bpmn.instance.CallActivity;
import io.zeebe.model.bpmn.instance.CompensateEventDefinition;
import io.zeebe.model.bpmn.instance.ConditionExpression;
import io.zeebe.model.bpmn.instance.EndEvent;
import io.zeebe.model.bpmn.instance.EventBasedGateway;
import io.zeebe.model.bpmn.instance.EventDefinition;
import io.zeebe.model.bpmn.instance.ExclusiveGateway;
import io.zeebe.model.bpmn.instance.FlowNode;
import io.zeebe.model.bpmn.instance.Gateway;
import io.zeebe.model.bpmn.instance.InclusiveGateway;
import io.zeebe.model.bpmn.instance.IntermediateCatchEvent;
import io.zeebe.model.bpmn.instance.IntermediateThrowEvent;
import io.zeebe.model.bpmn.instance.ManualTask;
import io.zeebe.model.bpmn.instance.ParallelGateway;
import io.zeebe.model.bpmn.instance.ReceiveTask;
import io.zeebe.model.bpmn.instance.ScriptTask;
import io.zeebe.model.bpmn.instance.SendTask;
import io.zeebe.model.bpmn.instance.SequenceFlow;
import io.zeebe.model.bpmn.instance.ServiceTask;
import io.zeebe.model.bpmn.instance.SubProcess;
import io.zeebe.model.bpmn.instance.Transaction;
import io.zeebe.model.bpmn.instance.UserTask;
import io.zeebe.model.bpmn.instance.bpmndi.BpmnShape;
import java.util.function.Consumer;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;

/** @author Sebastian Menski */
public abstract class AbstractFlowNodeBuilder<
        B extends AbstractFlowNodeBuilder, E extends FlowNode>
    extends AbstractFlowElementBuilder {

  private SequenceFlowBuilder currentSequenceFlowBuilder;

  protected boolean compensationStarted;
  protected BoundaryEvent compensateBoundaryEvent;

  protected AbstractFlowNodeBuilder(BpmnModelInstance modelInstance, E element, Class selfType) {
    super(modelInstance, element, selfType);
  }

  protected SequenceFlowBuilder getCurrentSequenceFlowBuilder() {
    if (currentSequenceFlowBuilder == null) {
      final SequenceFlow sequenceFlow = createSibling(SequenceFlow.class);
      currentSequenceFlowBuilder = sequenceFlow.builder();
    }
    return currentSequenceFlowBuilder;
  }

  public B condition(String name, String condition) {
    if (name != null) {
      getCurrentSequenceFlowBuilder().name(name);
    }
    final ConditionExpression conditionExpression = createInstance(ConditionExpression.class);
    conditionExpression.setTextContent(condition);
    getCurrentSequenceFlowBuilder().condition(conditionExpression);
    return myself;
  }

  public B condition(String condition) {
    return condition(null, condition);
  }

  protected void connectTarget(FlowNode target) {
    // check if compensation was started
    if (isBoundaryEventWithStartedCompensation()) {
      // the target activity should be marked for compensation
      if (target instanceof Activity) {
        ((Activity) target).setForCompensation(true);
      }

      // connect the target via association instead of sequence flow
      connectTargetWithAssociation(target);
    } else if (isCompensationHandler()) {
      // cannot connect to a compensation handler
      throw new BpmnModelException(
          "Only single compensation handler allowed. Call compensationDone() to continue main flow.");
    } else {
      // connect as sequence flow by default
      connectTargetWithSequenceFlow(target);
    }
  }

  protected void connectTargetWithSequenceFlow(FlowNode target) {
    getCurrentSequenceFlowBuilder().from(element).to(target);

    final SequenceFlow sequenceFlow = getCurrentSequenceFlowBuilder().getElement();
    createEdge(sequenceFlow);
    currentSequenceFlowBuilder = null;
  }

  protected void connectTargetWithAssociation(FlowNode target) {
    final Association association = modelInstance.newInstance(Association.class);
    association.setTarget(target);
    association.setSource(element);
    association.setAssociationDirection(AssociationDirection.One);
    element.getParentElement().addChildElement(association);

    createEdge(association);
  }

  public AbstractFlowNodeBuilder compensationDone() {
    if (compensateBoundaryEvent != null) {
      return compensateBoundaryEvent.getAttachedTo().builder();
    } else {
      throw new BpmnModelException("No compensation in progress. Call compensationStart() first.");
    }
  }

  public B sequenceFlowId(String sequenceFlowId) {
    getCurrentSequenceFlowBuilder().id(sequenceFlowId);
    return myself;
  }

  private  T createTarget(Class typeClass) {
    return createTarget(typeClass, null);
  }

  protected  T createTarget(Class typeClass, String identifier) {
    final T target = createSibling(typeClass, identifier);

    final BpmnShape targetBpmnShape = createBpmnShape(target);
    setCoordinates(targetBpmnShape);
    connectTarget(target);
    resizeSubProcess(targetBpmnShape);
    return target;
  }

  protected  T createTargetBuilder(
      Class typeClass) {
    return createTargetBuilder(typeClass, null);
  }

  @SuppressWarnings("unchecked")
  protected  T createTargetBuilder(
      Class typeClass, String id) {
    final AbstractFlowNodeBuilder builder = createTarget(typeClass, id).builder();

    if (compensationStarted) {
      // pass on current boundary event to return after compensationDone call
      builder.compensateBoundaryEvent = compensateBoundaryEvent;
    }

    return (T) builder;
  }

  public ServiceTaskBuilder serviceTask() {
    return createTargetBuilder(ServiceTask.class);
  }

  public ServiceTaskBuilder serviceTask(String id) {
    return createTargetBuilder(ServiceTask.class, id);
  }

  public ServiceTaskBuilder serviceTask(String id, Consumer consumer) {
    final ServiceTaskBuilder builder = createTargetBuilder(ServiceTask.class, id);
    consumer.accept(builder);
    return builder;
  }

  public SendTaskBuilder sendTask() {
    return createTargetBuilder(SendTask.class);
  }

  public SendTaskBuilder sendTask(String id) {
    return createTargetBuilder(SendTask.class, id);
  }

  public UserTaskBuilder userTask() {
    return createTargetBuilder(UserTask.class);
  }

  public UserTaskBuilder userTask(String id) {
    return createTargetBuilder(UserTask.class, id);
  }

  public BusinessRuleTaskBuilder businessRuleTask() {
    return createTargetBuilder(BusinessRuleTask.class);
  }

  public BusinessRuleTaskBuilder businessRuleTask(String id) {
    return createTargetBuilder(BusinessRuleTask.class, id);
  }

  public ScriptTaskBuilder scriptTask() {
    return createTargetBuilder(ScriptTask.class);
  }

  public ScriptTaskBuilder scriptTask(String id) {
    return createTargetBuilder(ScriptTask.class, id);
  }

  public ReceiveTaskBuilder receiveTask() {
    return createTargetBuilder(ReceiveTask.class);
  }

  public ReceiveTaskBuilder receiveTask(String id) {
    return createTargetBuilder(ReceiveTask.class, id);
  }

  public ManualTaskBuilder manualTask() {
    return createTargetBuilder(ManualTask.class);
  }

  public ManualTaskBuilder manualTask(String id) {
    return createTargetBuilder(ManualTask.class, id);
  }

  public EndEventBuilder endEvent() {
    return createTarget(EndEvent.class).builder();
  }

  public EndEventBuilder endEvent(String id) {
    return createTarget(EndEvent.class, id).builder();
  }

  public ParallelGatewayBuilder parallelGateway() {
    return createTarget(ParallelGateway.class).builder();
  }

  public ParallelGatewayBuilder parallelGateway(String id) {
    return createTarget(ParallelGateway.class, id).builder();
  }

  public ExclusiveGatewayBuilder exclusiveGateway() {
    return createTarget(ExclusiveGateway.class).builder();
  }

  public InclusiveGatewayBuilder inclusiveGateway() {
    return createTarget(InclusiveGateway.class).builder();
  }

  public EventBasedGatewayBuilder eventBasedGateway() {
    return createTarget(EventBasedGateway.class).builder();
  }

  public EventBasedGatewayBuilder eventBasedGateway(String id) {
    return createTarget(EventBasedGateway.class, id).builder();
  }

  public ExclusiveGatewayBuilder exclusiveGateway(String id) {
    return createTarget(ExclusiveGateway.class, id).builder();
  }

  public InclusiveGatewayBuilder inclusiveGateway(String id) {
    return createTarget(InclusiveGateway.class, id).builder();
  }

  public IntermediateCatchEventBuilder intermediateCatchEvent() {
    return createTarget(IntermediateCatchEvent.class).builder();
  }

  public IntermediateCatchEventBuilder intermediateCatchEvent(String id) {
    return createTarget(IntermediateCatchEvent.class, id).builder();
  }

  public IntermediateCatchEventBuilder intermediateCatchEvent(
      String id, Consumer builderConsumer) {
    final IntermediateCatchEventBuilder builder =
        createTarget(IntermediateCatchEvent.class, id).builder();
    builderConsumer.accept(builder);
    return builder;
  }

  public IntermediateThrowEventBuilder intermediateThrowEvent() {
    return createTarget(IntermediateThrowEvent.class).builder();
  }

  public IntermediateThrowEventBuilder intermediateThrowEvent(String id) {
    return createTarget(IntermediateThrowEvent.class, id).builder();
  }

  public CallActivityBuilder callActivity() {
    return createTarget(CallActivity.class).builder();
  }

  public CallActivityBuilder callActivity(String id) {
    return createTarget(CallActivity.class, id).builder();
  }

  public SubProcessBuilder subProcess() {
    return createTarget(SubProcess.class).builder();
  }

  public SubProcessBuilder subProcess(String id) {
    return createTarget(SubProcess.class, id).builder();
  }

  public SubProcessBuilder subProcess(String id, Consumer consumer) {

    final SubProcessBuilder builder = createTarget(SubProcess.class, id).builder();
    consumer.accept(builder);
    return builder;
  }

  public TransactionBuilder transaction() {
    final Transaction transaction = createTarget(Transaction.class);
    return new TransactionBuilder(modelInstance, transaction);
  }

  public TransactionBuilder transaction(String id) {
    final Transaction transaction = createTarget(Transaction.class, id);
    return new TransactionBuilder(modelInstance, transaction);
  }

  private  T findLastGateway(Class gatewayType) {
    FlowNode lastGateway = element;
    while (true) {
      try {
        lastGateway = lastGateway.getPreviousNodes().singleResult();
        if (gatewayType.isAssignableFrom(lastGateway.getClass())) {
          return gatewayType.cast(lastGateway);
        }
      } catch (final BpmnModelException e) {
        throw new BpmnModelException(
            "Unable to determine an unique previous gateway of " + lastGateway.getId(), e);
      }
    }
  }

  public AbstractGatewayBuilder moveToLastGateway() {
    return findLastGateway(Gateway.class).builder();
  }

  public AbstractExclusiveGatewayBuilder moveToLastExclusiveGateway() {
    return findLastGateway(ExclusiveGateway.class).builder();
  }

  public AbstractFlowNodeBuilder moveToNode(String identifier) {
    final ModelElementInstance instance = modelInstance.getModelElementById(identifier);
    if (instance instanceof FlowNode) {
      return ((FlowNode) instance).builder();
    } else {
      throw new BpmnModelException("Flow node not found for id " + identifier);
    }
  }

  @SuppressWarnings("unchecked")
  public > T moveToActivity(String identifier) {
    final ModelElementInstance instance = modelInstance.getModelElementById(identifier);
    if (instance instanceof Activity) {
      return (T) ((Activity) instance).builder();
    } else {
      throw new BpmnModelException("Activity not found for id " + identifier);
    }
  }

  public AbstractFlowNodeBuilder connectTo(String identifier) {
    final ModelElementInstance target = modelInstance.getModelElementById(identifier);
    if (target == null) {
      throw new BpmnModelException(
          "Unable to connect "
              + element.getId()
              + " to element "
              + identifier
              + " cause it not exists.");
    } else if (!(target instanceof FlowNode)) {
      throw new BpmnModelException(
          "Unable to connect "
              + element.getId()
              + " to element "
              + identifier
              + " cause its not a flow node.");
    } else {
      final FlowNode targetNode = (FlowNode) target;
      connectTarget(targetNode);
      return targetNode.builder();
    }
  }

  public B compensationStart() {
    if (element instanceof BoundaryEvent) {
      final BoundaryEvent boundaryEvent = (BoundaryEvent) element;
      for (final EventDefinition eventDefinition : boundaryEvent.getEventDefinitions()) {
        if (eventDefinition instanceof CompensateEventDefinition) {
          // if the boundary event contains a compensate event definition then
          // save the boundary event to later return to it and start a compensation

          compensateBoundaryEvent = boundaryEvent;
          compensationStarted = true;

          return myself;
        }
      }
    }

    throw new BpmnModelException(
        "Compensation can only be started on a boundary event with a compensation event definition");
  }

  protected boolean isBoundaryEventWithStartedCompensation() {
    return compensationStarted && compensateBoundaryEvent != null;
  }

  protected boolean isCompensationHandler() {
    return !compensationStarted && compensateBoundaryEvent != null;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy