content: /*
   * Copyright 2010-2025 Hyland Software, Inc. and its affiliates.
   *
   * 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 org.activiti.engine.test.impl.logger;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
  import org.activiti.engine.test.impl.logger.DebugInfoExecutionTree.DebugInfoExecutionTreeNode;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
  
   */
  public class ProcessExecutionLogger {
  
      private static final Logger logger = LoggerFactory.getLogger(ProcessExecutionLogger.class);
  
      protected Map<String, List<DebugInfo>> debugInfoMap = new HashMap<String, List<DebugInfo>>();
  
      // To avoid going to the db (and thus influencing process execution/tests), we store all encountered executions here,
      // to build up a tree representation with that information afterwards.
      protected Map<String, ExecutionEntity> createdExecutions = new HashMap<String, ExecutionEntity>();
      protected Map<String, ExecutionEntity> deletedExecutions = new HashMap<String, ExecutionEntity>();
  
      public ProcessExecutionLogger() {}
  
      public void addDebugInfo(AbstractDebugInfo debugInfo) {
          addDebugInfo(debugInfo, false);
      }
  
      public synchronized void addDebugInfo(AbstractDebugInfo debugInfo, boolean generateExecutionTreeRepresentation) {
          // Store debug info
          String threadName = Thread.currentThread().getName();
          if (!debugInfoMap.containsKey(threadName)) {
              debugInfoMap.put(threadName, new ArrayList<DebugInfo>());
          }
          debugInfoMap.get(threadName).add(debugInfo);
  
          // Generate execution tree
          if (generateExecutionTreeRepresentation) {
              debugInfo.setExecutionTrees(generateExecutionTrees());
          }
      }
  
      protected List<DebugInfoExecutionTree> generateExecutionTrees() {
          // Gather information
          List<ExecutionEntity> processInstances = new ArrayList<ExecutionEntity>();
          Map<String, List<ExecutionEntity>> parentMapping = new HashMap<String, List<ExecutionEntity>>();
  
          for (ExecutionEntity executionEntity : createdExecutions.values()) {
              if (!deletedExecutions.containsKey(executionEntity.getId())) {
                  if (executionEntity.getParentId() == null) {
                      processInstances.add(executionEntity);
                  } else {
                      if (!parentMapping.containsKey(executionEntity.getParentId())) {
                          parentMapping.put(executionEntity.getParentId(), new ArrayList<ExecutionEntity>());
                      }
                      parentMapping.get(executionEntity.getParentId()).add(executionEntity);
                  }
              }
          }
  
          // Build tree representation
          List<DebugInfoExecutionTree> executionTrees = new ArrayList<DebugInfoExecutionTree>();
          for (ExecutionEntity processInstance : processInstances) {
              DebugInfoExecutionTree executionTree = new DebugInfoExecutionTree();
              executionTrees.add(executionTree);
  
              DebugInfoExecutionTreeNode rootNode = new DebugInfoExecutionTreeNode();
              executionTree.setProcessInstance(rootNode);
              rootNode.setId(processInstance.getId());
  
              internalPopulateExecutionTree(rootNode, parentMapping);
          }
  
          return executionTrees;
      }
  
      protected void internalPopulateExecutionTree(
          DebugInfoExecutionTreeNode parentNode,
          Map<String, List<ExecutionEntity>> parentMapping
      ) {
          if (parentMapping.containsKey(parentNode.getId())) {
              for (ExecutionEntity childExecutionEntity : parentMapping.get(parentNode.getId())) {
                  DebugInfoExecutionTreeNode childNode = new DebugInfoExecutionTreeNode();
                  childNode.setId(childExecutionEntity.getId());
                  childNode.setActivityId(
                      childExecutionEntity.getCurrentFlowElement() != null
                          ? childExecutionEntity.getCurrentFlowElement().getId()
                          : null
                  );
                  childNode.setActivityName(
                      childExecutionEntity.getCurrentFlowElement() != null
                          ? childExecutionEntity.getCurrentFlowElement().getName()
                          : null
                  );
                  childNode.setProcessDefinitionId(childExecutionEntity.getProcessDefinitionId());
  
                  childNode.setParentNode(childNode);
                  parentNode.getChildNodes().add(childNode);
  
                  internalPopulateExecutionTree(childNode, parentMapping);
              }
          }
      }
  
      public void logDebugInfo() {
          logDebugInfo(false);
      }
  
      public void logDebugInfo(boolean clearAfterLogging) {
          logger.info("--------------------------------");
          logger.info("CommandInvoker debug information");
          logger.info("--------------------------------");
          for (String threadName : debugInfoMap.keySet()) {
              logger.info("");
              logger.info("Thread '" + threadName + "':");
              logger.info("");
  
              for (DebugInfo debugInfo : debugInfoMap.get(threadName)) {
                  debugInfo.printOut(logger);
              }
          }
  
          logger.info("");
  
          if (clearAfterLogging) {
              clear();
          }
      }
  
      public void clear() {
          debugInfoMap.clear();
          createdExecutions.clear();
      }
  
      public void executionCreated(ExecutionEntity executionEntity) {
          createdExecutions.put(executionEntity.getId(), executionEntity);
      }
  
      public void executionDeleted(ExecutionEntity executionEntity) {
          deletedExecutions.put(executionEntity.getId(), executionEntity);
      }
  }
  