docs: public void parseBoundaryEvents(Element parentElement, ScopeImpl flowScope) {
      for (Element boundaryEventElement : parentElement.elements("boundaryEvent")) {
  
        // The boundary event is attached to an activity, reference by the
        // 'attachedToRef' attribute
        String attachedToRef = boundaryEventElement.attribute("attachedToRef");
        if (attachedToRef == null || attachedToRef.equals("")) {
          addError("AttachedToRef is required when using a timerEventDefinition", boundaryEventElement);
        }
  
        // Representation structure-wise is a nested activity in the activity to
        // which its attached
        String id = boundaryEventElement.attribute("id");
  
        LOG.parsingElement("boundary event", id);
  
        // Depending on the sub-element definition, the correct activityBehavior
        // parsing is selected
        Element timerEventDefinition = boundaryEventElement.element(TIMER_EVENT_DEFINITION);
        Element errorEventDefinition = boundaryEventElement.element(ERROR_EVENT_DEFINITION);
        Element signalEventDefinition = boundaryEventElement.element(SIGNAL_EVENT_DEFINITION);
        Element cancelEventDefinition = boundaryEventElement.element(CANCEL_EVENT_DEFINITION);
        Element compensateEventDefinition = boundaryEventElement.element(COMPENSATE_EVENT_DEFINITION);
        Element messageEventDefinition = boundaryEventElement.element(MESSAGE_EVENT_DEFINITION);
        Element escalationEventDefinition = boundaryEventElement.element(ESCALATION_EVENT_DEFINITION);
        Element conditionalEventDefinition = boundaryEventElement.element(CONDITIONAL_EVENT_DEFINITION);
  
        // create the boundary event activity
        ActivityImpl boundaryEventActivity = createActivityOnScope(boundaryEventElement, flowScope);
        parseAsynchronousContinuation(boundaryEventElement, boundaryEventActivity);
  
        ActivityImpl attachedActivity = flowScope.findActivityAtLevelOfSubprocess(attachedToRef);
        if (attachedActivity == null) {
          addError("Invalid reference in boundary event. Make sure that the referenced activity is defined in the same scope as the boundary event",
              boundaryEventElement);
        }
  
        // determine the correct event scope (the scope in which the boundary event catches events)
        if (compensateEventDefinition == null) {
          ActivityImpl multiInstanceScope = getMultiInstanceScope(attachedActivity);
          if (multiInstanceScope != null) {
            // if the boundary event is attached to a multi instance activity,
            // then the scope of the boundary event is the multi instance body.
            boundaryEventActivity.setEventScope(multiInstanceScope);
          } else {
            attachedActivity.setScope(true);
            boundaryEventActivity.setEventScope(attachedActivity);
          }
        } else {
          boundaryEventActivity.setEventScope(attachedActivity);
        }
  
        // except escalation, by default is assumed to abort the activity
        String cancelActivityAttr = boundaryEventElement.attribute("cancelActivity", TRUE);
        boolean isCancelActivity = Boolean.valueOf(cancelActivityAttr);
  
        // determine start behavior
        if (isCancelActivity) {
          boundaryEventActivity.setActivityStartBehavior(ActivityStartBehavior.CANCEL_EVENT_SCOPE);
        } else {
          boundaryEventActivity.setActivityStartBehavior(ActivityStartBehavior.CONCURRENT_IN_FLOW_SCOPE);
        }
  
        // Catch event behavior is the same for most types
        ActivityBehavior behavior = new BoundaryEventActivityBehavior();
        if (timerEventDefinition != null) {
          parseBoundaryTimerEventDefinition(timerEventDefinition, isCancelActivity, boundaryEventActivity);
  
        } else if (errorEventDefinition != null) {
          parseBoundaryErrorEventDefinition(errorEventDefinition, boundaryEventActivity);
  
        } else if (signalEventDefinition != null) {
          parseBoundarySignalEventDefinition(signalEventDefinition, isCancelActivity, boundaryEventActivity);
  
        } else if (cancelEventDefinition != null) {
          behavior = parseBoundaryCancelEventDefinition(cancelEventDefinition, boundaryEventActivity);
  
        } else if (compensateEventDefinition != null) {
          parseBoundaryCompensateEventDefinition(compensateEventDefinition, boundaryEventActivity);
  
        } else if (messageEventDefinition != null) {
          parseBoundaryMessageEventDefinition(messageEventDefinition, isCancelActivity, boundaryEventActivity);
  
        } else if (escalationEventDefinition != null) {
  
          if (attachedActivity.isSubProcessScope() || attachedActivity.getActivityBehavior() instanceof CallActivityBehavior) {
            parseBoundaryEscalationEventDefinition(escalationEventDefinition, isCancelActivity, boundaryEventActivity);
          } else {
            addError("An escalation boundary event should only be attached to a subprocess or a call activity", boundaryEventElement);
          }
  
        } else if (conditionalEventDefinition != null) {
          behavior = parseBoundaryConditionalEventDefinition(conditionalEventDefinition, isCancelActivity, boundaryEventActivity);
        } else {
          addError("Unsupported boundary event type", boundaryEventElement);
  
        }
  
        ensureNoIoMappingDefined(boundaryEventElement);
  
        boundaryEventActivity.setActivityBehavior(behavior);
  
        parseExecutionListenersOnScope(boundaryEventElement, boundaryEventActivity);
  
        for (BpmnParseListener parseListener : parseListeners) {
          parseListener.parseBoundaryEvent(boundaryEventElement, flowScope, boundaryEventActivity);
        }
  
      }
  
    }