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

io.camunda.zeebe.model.bpmn.util.ModelUtil Maven / Gradle / Ivy

There is a newer version: 8.7.0-alpha2
Show newest version
/*
 * 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.camunda.zeebe.model.bpmn.util;

import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;

import io.camunda.zeebe.model.bpmn.instance.Activity;
import io.camunda.zeebe.model.bpmn.instance.BoundaryEvent;
import io.camunda.zeebe.model.bpmn.instance.Error;
import io.camunda.zeebe.model.bpmn.instance.ErrorEventDefinition;
import io.camunda.zeebe.model.bpmn.instance.EventDefinition;
import io.camunda.zeebe.model.bpmn.instance.Message;
import io.camunda.zeebe.model.bpmn.instance.MessageEventDefinition;
import io.camunda.zeebe.model.bpmn.instance.StartEvent;
import io.camunda.zeebe.model.bpmn.instance.SubProcess;
import io.camunda.zeebe.model.bpmn.instance.TimerEventDefinition;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;

public class ModelUtil {

  private static final List> NON_INTERRUPTING_EVENT_DEFINITIONS =
      Arrays.asList(MessageEventDefinition.class, TimerEventDefinition.class);

  public static List getEventDefinitionsForBoundaryEvents(final Activity element) {
    return element.getBoundaryEvents().stream()
        .flatMap(event -> event.getEventDefinitions().stream())
        .collect(Collectors.toList());
  }

  public static List getEventDefinitionsForEventSubprocesses(
      final ModelElementInstance element) {
    return element.getChildElementsByType(SubProcess.class).stream()
        .filter(SubProcess::triggeredByEvent)
        .flatMap(subProcess -> subProcess.getChildElementsByType(StartEvent.class).stream())
        .flatMap(s -> s.getEventDefinitions().stream())
        .collect(Collectors.toList());
  }

  public static List getDuplicateMessageNames(
      final Stream eventDefinitions) {

    final Stream messages =
        eventDefinitions
            .map(MessageEventDefinition::getMessage)
            .filter(m -> m.getName() != null && !m.getName().isEmpty());

    return messages.collect(groupingBy(Message::getName, counting())).entrySet().stream()
        .filter(e -> e.getValue() > 1)
        .map(Entry::getKey)
        .collect(Collectors.toList());
  }

  public static void verifyNoDuplicatedBoundaryEvents(
      final Activity activity, final Consumer errorCollector) {

    final List definitions = getEventDefinitionsForBoundaryEvents(activity);

    verifyNoDuplicatedEventDefinition(definitions, errorCollector);
  }

  public static void verifyEventDefinition(
      final BoundaryEvent boundaryEvent, final Consumer errorCollector) {
    boundaryEvent
        .getEventDefinitions()
        .forEach(
            definition ->
                verifyEventDefinition(definition, boundaryEvent.cancelActivity(), errorCollector));
  }

  public static void verifyEventDefinition(
      final StartEvent startEvent, final Consumer errorCollector) {

    startEvent
        .getEventDefinitions()
        .forEach(
            definition ->
                verifyEventDefinition(definition, startEvent.isInterrupting(), errorCollector));
  }

  public static void verifyNoDuplicatedEventSubprocesses(
      final ModelElementInstance element, final Consumer errorCollector) {

    final List definitions = getEventDefinitionsForEventSubprocesses(element);

    verifyNoDuplicatedEventDefinition(definitions, errorCollector);
  }

  public static void verifyNoDuplicatedEventDefinition(
      final Collection definitions,
      final Consumer errorCollector) {

    final Stream messageNames =
        getEventDefinition(definitions, MessageEventDefinition.class)
            .filter(def -> def.getMessage() != null)
            .map(MessageEventDefinition::getMessage)
            .filter(message -> message.getName() != null && !message.getName().isEmpty())
            .map(Message::getName);

    getDuplicatedEntries(messageNames)
        .map(ModelUtil::duplicatedMessageNames)
        .forEach(errorCollector);

    final Stream errorCodes =
        getEventDefinition(definitions, ErrorEventDefinition.class)
            .filter(def -> def.getError() != null)
            .map(ErrorEventDefinition::getError)
            .filter(error -> error.getErrorCode() != null && !error.getErrorCode().isEmpty())
            .map(Error::getErrorCode);

    getDuplicatedEntries(errorCodes).map(ModelUtil::duplicatedErrorCodes).forEach(errorCollector);
  }

  public static  Stream getEventDefinition(
      final Collection collection, final Class type) {
    return collection.stream().filter(type::isInstance).map(type::cast);
  }

  public static Stream getDuplicatedEntries(final Stream stream) {
    return stream.collect(groupingBy(Function.identity(), counting())).entrySet().stream()
        .filter(e -> e.getValue() > 1)
        .map(Entry::getKey);
  }

  private static String duplicatedMessageNames(final String messageName) {
    return String.format(
        "Multiple message event definitions with the same name '%s' are not allowed.", messageName);
  }

  private static String duplicatedErrorCodes(final String errorCode) {
    return String.format(
        "Multiple error event definitions with the same errorCode '%s' are not allowed.",
        errorCode);
  }

  private static void verifyEventDefinition(
      final EventDefinition definition,
      final boolean isInterrupting,
      final Consumer errorCollector) {

    if (!isInterrupting
        && !NON_INTERRUPTING_EVENT_DEFINITIONS.contains(
            definition.getElementType().getInstanceType())) {
      errorCollector.accept("Non-Interrupting event of this type is not allowed");
    }

    if (isInterrupting && definition instanceof TimerEventDefinition) {
      final TimerEventDefinition timerEventDefinition = (TimerEventDefinition) definition;
      if (timerEventDefinition.getTimeCycle() != null) {
        errorCollector.accept("Interrupting timer event with time cycle is not allowed.");
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy