From bb0931e8e9a35aa47f6a1fd1fd1b00d0220f803b Mon Sep 17 00:00:00 2001 From: Trung Mai Date: Thu, 4 Jul 2024 16:34:42 +0700 Subject: [PATCH] TE-624: Check next element of parallel task --- .../processes/FlowParallelInOrder.p.json | 61 ++++++++++--- .../test/FlowParallelInOrderCaseTest.java | 6 +- .../test/FlowParallelInOrderTest.java | 2 +- .../inspector/internal/PathFinder.java | 74 ++++++++++++---- .../inspector/internal/ProcessGraph.java | 9 ++ .../inspector/internal/WorkflowPath.java | 88 ++++++++----------- .../internal/helper/AnalysisPathHelper.java | 85 +++++++++++++++--- .../internal/model/TaskParallelGroup.java | 15 ++-- 8 files changed, 233 insertions(+), 107 deletions(-) diff --git a/process-inspector-test/processes/FlowParallelInOrder.p.json b/process-inspector-test/processes/FlowParallelInOrder.p.json index ba33259..98a0925 100644 --- a/process-inspector-test/processes/FlowParallelInOrder.p.json +++ b/process-inspector-test/processes/FlowParallelInOrder.p.json @@ -12,7 +12,7 @@ "signature" : "start" }, "visual" : { - "at" : { "x" : 128, "y" : 168 } + "at" : { "x" : 128, "y" : 104 } }, "connect" : [ { "id" : "f2", "to" : "f3", "var" : "in1" } @@ -21,7 +21,7 @@ "id" : "f1", "type" : "TaskEnd", "visual" : { - "at" : { "x" : 864, "y" : 168 } + "at" : { "x" : 736, "y" : 104 } } }, { "id" : "f3", @@ -48,15 +48,22 @@ "", "APAConfig.setEstimate(2,TimeUnit.HOURS,UseCase.SMALLPROJECT);" ] + }, { + "id" : "TaskC", + "name" : "Task 1C", + "responsible" : { + "activator" : "SYSTEM" + } } ] }, "visual" : { - "at" : { "x" : 200, "y" : 168 }, + "at" : { "x" : 200, "y" : 104 }, "labelOffset" : { "x" : -8, "y" : -8 } }, "connect" : [ - { "id" : "f5", "to" : "f4", "via" : [ { "x" : 200, "y" : 112 } ], "condition" : "ivp==\"TaskA.ivp\"" }, - { "id" : "f9", "to" : "f8", "via" : [ { "x" : 200, "y" : 224 } ], "condition" : "ivp==\"TaskB.ivp\"" } + { "id" : "f5", "to" : "f4", "via" : [ { "x" : 200, "y" : 56 } ], "condition" : "ivp==\"TaskA.ivp\"" }, + { "id" : "f9", "to" : "f8", "via" : [ { "x" : 216, "y" : 144 } ], "condition" : "ivp==\"TaskB.ivp\"" }, + { "id" : "f132", "to" : "f115", "via" : [ { "x" : 200, "y" : 224 } ], "condition" : "ivp==\"TaskC.ivp\"" } ] }, { "id" : "f4", @@ -77,7 +84,7 @@ } }, "visual" : { - "at" : { "x" : 312, "y" : 112 } + "at" : { "x" : 280, "y" : 56 } }, "connect" : [ { "id" : "f7", "to" : "f6" } @@ -101,10 +108,10 @@ } }, "visual" : { - "at" : { "x" : 480, "y" : 112 } + "at" : { "x" : 432, "y" : 56 } }, "connect" : [ - { "id" : "f11", "to" : "f10", "via" : [ { "x" : 576, "y" : 112 } ], "var" : "in1" } + { "id" : "f11", "to" : "f10", "via" : [ { "x" : 520, "y" : 56 } ], "var" : "in1" } ] }, { "id" : "f8", @@ -125,10 +132,10 @@ } }, "visual" : { - "at" : { "x" : 400, "y" : 224 } + "at" : { "x" : 352, "y" : 144 } }, "connect" : [ - { "id" : "f12", "to" : "f10", "via" : [ { "x" : 576, "y" : 224 } ], "var" : "in2" } + { "id" : "f12", "to" : "f10", "via" : [ { "x" : 520, "y" : 144 } ], "var" : "in2" } ] }, { "id" : "f10", @@ -143,7 +150,7 @@ } ] }, "visual" : { - "at" : { "x" : 576, "y" : 168 }, + "at" : { "x" : 520, "y" : 104 }, "labelOffset" : { "x" : -16, "y" : 24 } }, "connect" : [ @@ -160,7 +167,7 @@ } }, "visual" : { - "at" : { "x" : 712, "y" : 168 } + "at" : { "x" : 624, "y" : 104 } }, "connect" : [ { "id" : "f15", "to" : "f1" } @@ -1093,6 +1100,36 @@ "connect" : [ { "id" : "f114", "to" : "f102", "var" : "in2" } ] + }, { + "id" : "f115", + "type" : "UserTask", + "name" : "Task E", + "config" : { + "dialog" : "com.axonivy.utils.process.inspector.test.Dummy:start()", + "task" : { + "name" : "Task E", + "code" : [ + "import com.axonivy.utils.process.inspector.test.UseCase;", + "import com.axonivy.utils.process.inspector.APAConfig;", + "import java.util.concurrent.TimeUnit;", + "", + "APAConfig.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);", + "APAConfig.setEstimate(3,TimeUnit.HOURS,UseCase.SMALLPROJECT);" + ] + } + }, + "visual" : { + "at" : { "x" : 360, "y" : 224 } + }, + "connect" : [ + { "id" : "f144", "to" : "f135" } + ] + }, { + "id" : "f135", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 552, "y" : 224 } + } } ], "layout" : { "colors" : { diff --git a/process-inspector-test/src_test/com/axonivy/utils/process/inspector/test/FlowParallelInOrderCaseTest.java b/process-inspector-test/src_test/com/axonivy/utils/process/inspector/test/FlowParallelInOrderCaseTest.java index 37eb04e..f8a4cd5 100644 --- a/process-inspector-test/src_test/com/axonivy/utils/process/inspector/test/FlowParallelInOrderCaseTest.java +++ b/process-inspector-test/src_test/com/axonivy/utils/process/inspector/test/FlowParallelInOrderCaseTest.java @@ -37,7 +37,7 @@ void shouldshouldFindAllTasksAtStart(BpmClient bpmClient) throws Exception { var detectedTasks = processInspector.findAllTasks(icase, null); - var expected = Arrays.array("Task 1A", "Task A", "Task B", "Task 1B", "Task C", "Task D"); + var expected = Arrays.array("Task E", "Task 1A", "Task A", "Task B", "Task 1B", "Task C", "Task D"); var taskNames = getTaskNames(detectedTasks); assertArrayEquals(expected, taskNames); } @@ -60,7 +60,7 @@ void shouldFindTasksOnPathByCaseAtTaskBAndTaskC(BpmClient bpmClient) throws Exce ExecutionResult result = bpmClient.start().process(FLOW_PARALLEL_IN_ORDER.elementName("start")).execute(); List parallelTasks = result.workflow().activeTasks(); for (ITask task : parallelTasks) { - result = bpmClient.start().task(task).as().everybody().execute(); + result = bpmClient.start().task(task).as().systemUser().execute(); } List activeTasks = result.workflow().activeTasks(); @@ -74,7 +74,7 @@ void shouldFindTasksOnPathByCaseAtTaskBAndTaskC(BpmClient bpmClient) throws Exce var detectedTasks = processInspector.findTasksOnPath(icase, null, null); - var expected = Arrays.array("Task C", "Task B", "Task D"); + var expected = Arrays.array("Task E", "Task C", "Task B", "Task D"); var taskNames = getTaskNames(detectedTasks); assertArrayEquals(expected, taskNames); diff --git a/process-inspector-test/src_test/com/axonivy/utils/process/inspector/test/FlowParallelInOrderTest.java b/process-inspector-test/src_test/com/axonivy/utils/process/inspector/test/FlowParallelInOrderTest.java index 34e9c39..0196df8 100644 --- a/process-inspector-test/src_test/com/axonivy/utils/process/inspector/test/FlowParallelInOrderTest.java +++ b/process-inspector-test/src_test/com/axonivy/utils/process/inspector/test/FlowParallelInOrderTest.java @@ -38,7 +38,7 @@ void shouldFindAllTasksAtStart() throws Exception { List detectedTasks = processInspector.findAllTasks(start, null).stream() .map(DetectedTask.class::cast).toList(); - var expected = Arrays.array("Task 1A", "Task A", "Task B", "Task 1B", "Task C", "Task D"); + var expected = Arrays.array("Task E", "Task 1A", "Task A", "Task B", "Task 1B", "Task C", "Task D"); var taskNames = getTaskNames(detectedTasks); assertArrayEquals(expected, taskNames); } diff --git a/process-inspector/src/com/axonivy/utils/process/inspector/internal/PathFinder.java b/process-inspector/src/com/axonivy/utils/process/inspector/internal/PathFinder.java index b378717..325a767 100644 --- a/process-inspector/src/com/axonivy/utils/process/inspector/internal/PathFinder.java +++ b/process-inspector/src/com/axonivy/utils/process/inspector/internal/PathFinder.java @@ -2,10 +2,13 @@ import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.addAllToPath; import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.addToPath; +import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.convertToAnalysisPath; import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.findIncomingsFromPaths; import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.getAllStartElementOfTaskSwitchGateways; import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.getAnalysisPathFrom; import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.getAnalysisPathTo; +import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.getInternalPath; +import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.getLastProcessElements; import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.getPathByStartElements; import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.replaceFirstElement; import static java.util.Collections.emptyList; @@ -15,6 +18,7 @@ import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import static org.apache.commons.lang3.StringUtils.isEmpty; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -166,7 +170,7 @@ private Map> mergePath(Map fullPathWithIntersection = addToPath(List.of(new AnalysisPath(List.of(taskGroup))), subPath); + List fullPathWithIntersection = addToPath(new AnalysisPath(List.of(taskGroup)), subPath); Map> result = new LinkedHashMap<>(); result.putAll(pathNotIntersection); @@ -231,7 +235,7 @@ private List convertToAnalysisPaths(Map subPathAfterIntersection = getAnalysisPathFrom(paths, intersection); - result.addAll(addToPath(List.of(new AnalysisPath(List.of(taskGroup))), subPathAfterIntersection)); + result.addAll(addToPath(new AnalysisPath(List.of(taskGroup)), subPathAfterIntersection)); } return result; @@ -251,15 +255,14 @@ private Map> getPathHaveNoIntersection( } private TaskParallelGroup convertToTaskParallelGroupWithInternalPath(Map> internalPaths) { - TaskParallelGroup taskGroup = new TaskParallelGroup(null); - + Map> result = new LinkedHashMap<>(); internalPaths.entrySet().forEach(it -> { SequenceFlow sequenceFlow = processGraph.getFirstIncoming(it.getKey().getElement()); result.put(sequenceFlow, it.getValue()); }); - taskGroup.setInternalPaths(result); + TaskParallelGroup taskGroup = new TaskParallelGroup(null, convertToAnalysisPath(result)); return taskGroup; } @@ -374,23 +377,55 @@ private Map> findAnalysisPathForNextNode(Proces return pathOptions; } - private TaskParallelGroup getTaskParallelGroup(ProcessElement from, String flowName, FindType findType, - List currentPath) throws Exception { - TaskParallelGroup result = new TaskParallelGroup(from.getElement()); + private TaskParallelGroup getTaskParallelGroup(ProcessElement from, String flowName, FindType findType, List currentPath) throws Exception { + List outs = getSequenceFlows((NodeElement) from.getElement(), flowName, findType); - Map> paths = new LinkedHashMap<>(); + Map> paths = new LinkedHashMap<>(); for (SequenceFlow out : outs) { CommonElement outElement = new CommonElement(out); List newPath = addAllToPath(currentPath, Arrays.asList(from, outElement)); ProcessElement nextStartElement = new CommonElement(out.getTarget()); List nextOfPath = findAnalysisPaths(nextStartElement, flowName, findType, newPath); - paths.put(out, nextOfPath); + paths.put(out, nextOfPath); } + + List convertedPaths = convertToAnalysisPath(paths); + List lastElements = getLastProcessElements(new TaskParallelGroup(from.getElement(), convertedPaths)); + ProcessElement joinTask = lastElements.stream().filter(it -> it.getElement()instanceof TaskSwitchGateway ).findFirst().orElse(null); + + List internalPaths = new ArrayList<>() ; + //Map> internalPaths = new LinkedHashMap<>(); + //Check join incoming is less then split task + if (joinTask != null && ((NodeElement) joinTask.getElement()).getIncoming().size() < outs.size()) { + List startPathWithoutTaskEnd = getInternalPath(convertedPaths, false).stream() + .map(AnalysisPathHelper::getFirsElement) + .map(ProcessElement::getElement) + .filter(SequenceFlow.class::isInstance) + .map(SequenceFlow.class::cast) + .distinct().toList(); + + Map> subInteralPaths = new LinkedHashMap<>(); + for(Entry> entry : paths.entrySet()) { + if(startPathWithoutTaskEnd.contains(entry.getKey())) { + subInteralPaths.put(entry.getKey(), entry.getValue()); + } else { + internalPaths.addAll(addToPath(new AnalysisPath(new CommonElement(entry.getKey())), entry.getValue())); + } + } + + TaskParallelGroup subTaskGroup = new TaskParallelGroup(from.getElement(),convertToAnalysisPath(subInteralPaths)); - result.setInternalPaths(paths); - + List nextPaths = findAnalysisPaths(joinTask, flowName, findType, emptyList()); + var newInternalPaths = addToPath(new AnalysisPath(subTaskGroup), nextPaths); + + internalPaths.addAll(newInternalPaths); + } else { + internalPaths = convertToAnalysisPath(paths); + } + + TaskParallelGroup result = new TaskParallelGroup(from.getElement(), internalPaths); return result; } @@ -613,6 +648,7 @@ private List correctInternalPathByNextSequence(List return result; } + private List getAnalysisPathBaseOnNextSequenceFlow(List internalPaths, ProcessElement nextElement) { if(nextElement == null) { return internalPaths; @@ -631,19 +667,19 @@ private List getAnalysisPathBaseOnNextSequenceFlow(List> validInternalPaths = new LinkedHashMap<>(); - ((TaskParallelGroup) lastProcessElement).getInternalPaths().forEach((key, value) -> { - var validPaths = getAnalysisPathBaseOnNextSequenceFlow(value, nextElement); + //Map> validInternalPaths = new LinkedHashMap<>(); + List validInternalPaths = new ArrayList<>(); + ((TaskParallelGroup) lastProcessElement).getPaths().forEach(value -> { + var validPaths = getAnalysisPathBaseOnNextSequenceFlow(List.of(value), nextElement); if (isNotEmpty(validPaths)) { - validInternalPaths.put(key, validPaths); + validInternalPaths.addAll(validPaths); } }); int size = path.getElements().size(); List newPath = path.getElements().stream().limit(size - 1).collect(Collectors.toList()); - if (MapUtils.isNotEmpty(validInternalPaths)) { - TaskParallelGroup taskGroup = new TaskParallelGroup(lastElement); - taskGroup.setInternalPaths(validInternalPaths); + if (isNotEmpty(validInternalPaths)) { + TaskParallelGroup taskGroup = new TaskParallelGroup(lastElement, validInternalPaths); newPath.add(taskGroup); } result.add(new AnalysisPath(newPath)); diff --git a/process-inspector/src/com/axonivy/utils/process/inspector/internal/ProcessGraph.java b/process-inspector/src/com/axonivy/utils/process/inspector/internal/ProcessGraph.java index 1c0d4af..cf4ed27 100644 --- a/process-inspector/src/com/axonivy/utils/process/inspector/internal/ProcessGraph.java +++ b/process-inspector/src/com/axonivy/utils/process/inspector/internal/ProcessGraph.java @@ -98,6 +98,15 @@ public TaskConfig getStartTaskConfig(SequenceFlow sequenceFlow) { return taskConfig; } + public boolean isSystemTask(TaskConfig task) { + if(task instanceof TaskConfig) { + String roleName = ((TaskConfig) task).getActivator().getName(); + return Role.SYSTEM.name().equals(roleName); + } + + return false; + } + public boolean isSystemTask(BaseElement task) { if (task instanceof TaskAndCaseModifier) { return ((TaskAndCaseModifier) task).getAllTaskConfigs().stream() diff --git a/process-inspector/src/com/axonivy/utils/process/inspector/internal/WorkflowPath.java b/process-inspector/src/com/axonivy/utils/process/inspector/internal/WorkflowPath.java index 920f05e..df2121a 100644 --- a/process-inspector/src/com/axonivy/utils/process/inspector/internal/WorkflowPath.java +++ b/process-inspector/src/com/axonivy/utils/process/inspector/internal/WorkflowPath.java @@ -1,5 +1,6 @@ package com.axonivy.utils.process.inspector.internal; +import static com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper.getInternalPath; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.stream.Collectors.toMap; @@ -9,7 +10,6 @@ import java.time.Duration; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -19,7 +19,6 @@ import org.apache.commons.collections4.map.HashedMap; import org.apache.commons.lang3.StringUtils; -import com.axonivy.utils.process.inspector.internal.helper.AnalysisPathHelper; import com.axonivy.utils.process.inspector.internal.model.AnalysisPath; import com.axonivy.utils.process.inspector.internal.model.CommonElement; import com.axonivy.utils.process.inspector.internal.model.DetectedEmbeddedEnd; @@ -93,6 +92,7 @@ private List convertToDetectedElements(Map result = detectedPaths.stream() .map(DetectedPath::getElements) .flatMap(List::stream) + .filter(it -> it instanceof DetectedTask || it instanceof DetectedAlternative) .toList(); result = keepMaxTimeUtilEndDetectedElement(result); @@ -260,32 +260,40 @@ private List convertTaskParallelGroupToDetectedPaths(TaskParallelG } private List convertTaskParallelGroupToDetectedPath(TaskParallelGroup group, Enum useCase, Map timeUntilStartAts, boolean withTaskEnd) { - Map> internalPath = getInternalPath(group.getInternalPaths(), withTaskEnd); + List internalPaths = getInternalPath(group.getPaths(), withTaskEnd); - List result = new ArrayList<>(); - for (Entry> entry : internalPath.entrySet()) { - SequenceFlow sequenceFlow = entry.getKey(); + List result = new ArrayList<>(); + for (AnalysisPath path : internalPaths) { + //Map> path = Map.of(key, entry.getValue()); + + ProcessElement startElement = path.getElements().get(0); + Map startTimeDuration = new HashedMap<>(timeUntilStartAts); + List subProcessElements = null; Duration startedAt = timeUntilStartAts.get(group); - - ProcessElement key = new CommonElement(sequenceFlow); - Map> path = Map.of(key, entry.getValue()); - Map startTimeDuration = null; - if (group.getElement() != null) { - var startTask = createStartTaskFromTaskSwitchGateway(sequenceFlow, startedAt, useCase); - if (startTask != null) { - result.add(new DetectedPath(startTask)); - startedAt = ((DetectedTask) startTask).getTimeUntilEnd(); + if (startElement.getElement() instanceof SequenceFlow) { + if (group.getElement() != null) { + SequenceFlow sequenceFlow = (SequenceFlow) startElement.getElement(); + var startTask = createStartTaskFromTaskSwitchGateway(sequenceFlow, startedAt, useCase); + if (startTask != null) { + result.add(new DetectedPath(startTask)); + startedAt = ((DetectedTask) startTask).getTimeUntilEnd(); + } + startTimeDuration.put(startElement, startedAt); + } else { + startElement = path.getElements().get(1); } + subProcessElements = path.getElements().stream().skip(1).toList(); - startTimeDuration = Map.of(key, startedAt); - - } else { - startTimeDuration = timeUntilStartAts; + + } else { + startElement = path.getElements().get(0); + startTimeDuration.put(startElement, startedAt); + subProcessElements = path.getElements(); } - - List detectedPaths = convertToDetectedPaths(path, useCase, startTimeDuration); - result.addAll(detectedPaths); + + DetectedPath detectedPath = convertDetectedPath(startElement, new AnalysisPath(subProcessElements), useCase, startTimeDuration); + result.add(detectedPath); } return result; @@ -342,11 +350,11 @@ private DetectedElement createStartTaskFromTaskSwitchGateway(SequenceFlow sequen Enum useCase) { DetectedElement task = null; if (sequenceFlow.getSource() instanceof TaskSwitchGateway) { - TaskSwitchGateway taskSwitchGateway = (TaskSwitchGateway) sequenceFlow.getSource(); - if (!processGraph.isSystemTask(taskSwitchGateway)) { - TaskConfig startTask = processGraph.getStartTaskConfig(sequenceFlow); + TaskConfig startTask = processGraph.getStartTaskConfig(sequenceFlow); + if(!processGraph.isSystemTask(startTask)) { + TaskSwitchGateway taskSwitchGateway = (TaskSwitchGateway) sequenceFlow.getSource(); task = createDetectedTask(taskSwitchGateway, startTask, useCase, timeUntilStartedAt); - } + } } return task; } @@ -484,33 +492,7 @@ private boolean isNotContains(List detectedElements, DetectedEl List pids = detectedElements.stream().map(DetectedElement::getPid).toList(); return !pids.contains(detectedElement.getPid()); } - - private Map> getInternalPath(Map> internalPath, boolean withTaskEnd) { - Map> paths = new LinkedHashMap<>(); - - // Priority the path go to end first - for (SequenceFlow sf : internalPath.keySet()) { - - List analysisPaths = internalPath.get(sf).stream() - .filter(it -> { - ProcessElement last = AnalysisPathHelper.getLastElement(it); - if (withTaskEnd && processGraph.isTaskEnd(last.getElement())) { - return true; - } else if (!withTaskEnd && !processGraph.isTaskEnd(last.getElement())) { - return true; - } else { - return false; - } - }).toList(); - - if (isNotEmpty(analysisPaths)) { - paths.put(sf, analysisPaths); - } - } - - return paths; - } - + private PathFinder pathFinder(Map startAtElements, String flowName) { return new PathFinder().setProcessFlowOverrides(processFlowOverrides).setFlowName(flowName) .setStartElements(startAtElements.keySet().stream().toList()); diff --git a/process-inspector/src/com/axonivy/utils/process/inspector/internal/helper/AnalysisPathHelper.java b/process-inspector/src/com/axonivy/utils/process/inspector/internal/helper/AnalysisPathHelper.java index cb35d4a..dab35f7 100644 --- a/process-inspector/src/com/axonivy/utils/process/inspector/internal/helper/AnalysisPathHelper.java +++ b/process-inspector/src/com/axonivy/utils/process/inspector/internal/helper/AnalysisPathHelper.java @@ -24,18 +24,23 @@ import ch.ivyteam.ivy.process.model.BaseElement; import ch.ivyteam.ivy.process.model.NodeElement; import ch.ivyteam.ivy.process.model.connector.SequenceFlow; +import ch.ivyteam.ivy.process.model.element.event.end.TaskEnd; import ch.ivyteam.ivy.process.model.element.event.start.RequestStart; import ch.ivyteam.ivy.process.model.element.gateway.TaskSwitchGateway; public class AnalysisPathHelper { + public static List addToPath(AnalysisPath path, List subPaths) { + return addToPath(List.of(path), subPaths); + } + public static List addToPath(List paths, List subPaths) { if (subPaths.isEmpty()) { return paths; } - List result = paths; + List result = new ArrayList<>(); for (AnalysisPath path : subPaths) { - result = addAllToPath(result, path.getElements()); + result.addAll(addAllToPath(paths, path.getElements())); } return result; @@ -56,8 +61,7 @@ public static List addAllToPath(List paths, List addAllToPath(List paths, - Map> pathOptions) { + public static List addAllToPath(List paths, Map> pathOptions) { List result = new ArrayList<>(); if (pathOptions.isEmpty()) { result.addAll(paths); @@ -104,6 +108,12 @@ public static ProcessElement getLastElement(AnalysisPath path) { int size = elements.size(); return size == 0 ? null : elements.get(size - 1); } + + public static ProcessElement getFirsElement(AnalysisPath path) { + List elements = path.getElements(); + int size = elements.size(); + return size == 0 ? null : elements.get(0); + } public static NodeElement getFirstNodeElement(List paths) { NodeElement startNode = AnalysisPathHelper.getAllProcessElement(paths).stream().map(ProcessElement::getElement) @@ -148,19 +158,49 @@ public static List getAllProcessElement(ProcessElement element) result.add(new CommonElement(group.getElement())); } - for (Entry> entry : group.getInternalPaths().entrySet()) { - List allProcessElement = entry.getValue().stream().map(AnalysisPath::getElements) - .flatMap(List::stream).flatMap(it -> getAllProcessElement(it).stream()).toList(); + //for (Entry> entry : group.getInternalPaths().entrySet()) { + List allProcessElement = group.getPaths().stream() + .map(AnalysisPath::getElements) + .flatMap(List::stream) + .flatMap(it -> getAllProcessElement(it).stream()) + .toList(); - result.add(new CommonElement(entry.getKey())); + //result.add(new CommonElement(entry.getKey())); result.addAll(allProcessElement); - } + return result; } return emptyList(); } + + public static List convertToAnalysisPath(Map> interalPaths) { + List result = new ArrayList<>(); + interalPaths.entrySet().forEach(it -> { + var paths = addToPath(List.of(new AnalysisPath(new CommonElement(it.getKey()))), it.getValue()); + result.addAll(paths); + }); + return result; + } + + public static List getLastProcessElements(TaskParallelGroup group) { + List result = new ArrayList<>(); + // for (AnalysisPath entry : group.getPaths()) { + // for (Entry> entry : + // group.getInternalPaths().entrySet()) { + List lastElements = new ArrayList<>(); + group.getPaths().stream().map(AnalysisPathHelper::getLastElement).forEach(it -> { + if (it instanceof TaskParallelGroup) { + result.addAll(getLastProcessElements((TaskParallelGroup) it)); + } else { + result.add(it); + } + }); + // } + + return result.stream().distinct().toList(); + } public static > Map getPathByStartElements(Map source, Set keys) { Map result = new LinkedHashMap<>(); @@ -193,7 +233,7 @@ public static List getAnalysisPathFrom(Map> getAnalysisPathTo(Map> source, ProcessElement to) { Map> pathBeforeIntersection = new LinkedHashMap<>(); for (Entry> entry : source.entrySet()) { - List beforeTo = getAnalysisPathTo(entry.getValue(), to); + List beforeTo = subAnalysisPathTo(entry.getValue(), to); if (isNotEmpty(beforeTo)) { pathBeforeIntersection.put(entry.getKey(), beforeTo); } @@ -258,7 +298,30 @@ public static Map> getAllStartElementOfTaskS return result; } - private static List getAnalysisPathTo(List source, ProcessElement to) { + + public static List getInternalPath(List internalPaths, boolean withTaskEnd) { + List paths = new ArrayList<>(); + + // Priority the path go to end first + List analysisPaths = internalPaths.stream().filter(it -> { + ProcessElement last = AnalysisPathHelper.getLastElement(it); + if (withTaskEnd && last.getElement() instanceof TaskEnd == true) { + return true; + } else if (!withTaskEnd && last.getElement() instanceof TaskEnd == false) { + return true; + } else { + return false; + } + }).toList(); + + if (isNotEmpty(analysisPaths)) { + paths.addAll(analysisPaths); + } + + return paths; + } + + private static List subAnalysisPathTo(List source, ProcessElement to) { List result = new ArrayList<>(); for (AnalysisPath path : source) { int index = path.getElements().indexOf(to); diff --git a/process-inspector/src/com/axonivy/utils/process/inspector/internal/model/TaskParallelGroup.java b/process-inspector/src/com/axonivy/utils/process/inspector/internal/model/TaskParallelGroup.java index a484946..c496008 100644 --- a/process-inspector/src/com/axonivy/utils/process/inspector/internal/model/TaskParallelGroup.java +++ b/process-inspector/src/com/axonivy/utils/process/inspector/internal/model/TaskParallelGroup.java @@ -3,27 +3,26 @@ import static org.apache.commons.lang3.StringUtils.EMPTY; import java.util.List; -import java.util.Map; import java.util.Objects; import ch.ivyteam.ivy.process.model.BaseElement; -import ch.ivyteam.ivy.process.model.connector.SequenceFlow; import ch.ivyteam.ivy.process.model.value.PID; public class TaskParallelGroup implements ProcessElement { private BaseElement element; - private Map> internalPaths; + private List internalPaths; - public TaskParallelGroup(BaseElement element) { + public TaskParallelGroup(BaseElement element, List internalPaths) { this.element = element; + this.internalPaths = internalPaths; } - - public Map> getInternalPaths() { + + public List getPaths() { return internalPaths; } - public void setInternalPaths(Map> internalPaths) { - this.internalPaths = internalPaths; + public void setPaths(List paths) { + this.internalPaths = paths; } @Override