diff --git a/open-bpmn.metamodel/pom.xml b/open-bpmn.metamodel/pom.xml
index 6db7c4e2..30d43314 100644
--- a/open-bpmn.metamodel/pom.xml
+++ b/open-bpmn.metamodel/pom.xml
@@ -11,25 +11,49 @@
UTF-8
- 11
+ 11
-
+
maven-compiler-plugin
3.8.1
-
+
+ maven-assembly-plugin
+
+
+
+ true
+ org.openbpmn.bpmn.discovery.BPMNDiscovery
+ lib/
+
+
+
+ jar-with-dependencies
+
+
+
-
- com.google.guava
- guava
- 13.0-rc1
-
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ 2.3.5
+
+
+ com.google.guava
+ guava
+ 13.0-rc1
+
+
+ org.jgrapht
+ jgrapht-core
+ 1.5.2
+
org.junit.jupiter
@@ -37,7 +61,6 @@
5.5.2
test
-
diff --git a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/BPMNModel.java b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/BPMNModel.java
index 031f5635..644f6e23 100644
--- a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/BPMNModel.java
+++ b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/BPMNModel.java
@@ -1948,4 +1948,46 @@ private void writeXml(Document doc, OutputStream output) throws TransformerExcep
// === BUGFIX END ===
}
+
+ public String getXml() throws TransformerException {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ DOMSource source = new DOMSource(doc);
+
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+
+ /*
+ * The following code section is to handle a bad implementation in the
+ * Imixs-BPMN (Eclipse-BPMN2) implementation
+ *
+ * To ensure that the Open-BPMN model file is still readable by eclipse-bpmn we
+ * need to remove the whitespace before and after CDATA tags.
+ *
+ * See details: https://github.com/imixs/open-bpmn/issues/194
+ *
+ * Otherwise we could just do here:
+ *
+ * StreamResult result = new StreamResult(output);
+ * transformer.transform(source, result);
+ */
+ // === BUGFIX START ===
+
+ // first transform the result xml into a string
+ StringWriter w = new StringWriter();
+ Result dest = new StreamResult(w);
+ transformer.transform(source, dest);
+ String xmlString = w.toString();
+ // No indentation (whitespace) for elements with a CDATA section.
+ // See
+ // https://stackoverflow.com/questions/55853220/handling-change-in-newlines-by-xml-transformation-for-cdata-from-java-8-to-java/75568933
+ // xmlString =
+ // xmlString.replaceAll(">\\s*+(<\\!\\[CDATA\\[(.|\\n|\\r\\n)*?]\\]>)\\s*",
+ // ">$1");
+ xmlString = BPMNXMLUtil.cleanCDATAWhiteSpace(xmlString);
+ return xmlString;
+ // write output
+
+ }
}
diff --git a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/BPMNDiscovery.java b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/BPMNDiscovery.java
index d1a75bf5..4025e8f8 100644
--- a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/BPMNDiscovery.java
+++ b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/BPMNDiscovery.java
@@ -1,6 +1,10 @@
package org.openbpmn.bpmn.discovery;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -8,10 +12,13 @@
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.openbpmn.bpmn.BPMNModel;
import org.openbpmn.bpmn.BPMNTypes;
+import org.openbpmn.bpmn.discovery.model.DepthFirstSearch;
import org.openbpmn.bpmn.elements.Activity;
import org.openbpmn.bpmn.elements.BPMNProcess;
import org.openbpmn.bpmn.elements.Gateway;
@@ -22,6 +29,14 @@
import org.openbpmn.bpmn.exceptions.BPMNModelException;
import org.openbpmn.bpmn.util.BPMNModelFactory;
+import io.process.analytics.tools.bpmn.generator.App;
+import io.process.analytics.tools.bpmn.generator.BPMNLayoutGenerator;
+import io.process.analytics.tools.bpmn.generator.BPMNLayoutGenerator.ExportType;
+import io.process.analytics.tools.bpmn.generator.internal.FileUtils;
+
+// mvn compile assembly:single
+// java -cp open-bpmn.metamodel-1.0.0-SNAPSHOT-jar-with-dependencies.jar;lib\* org.openbpmn.bpmn.discovery.BPMNDiscovery test/test.bpmn
+
public class BPMNDiscovery {
private static Logger logger = Logger.getLogger(BPMNDiscovery.class.getName());
static String PARALLEL = "parallel";
@@ -72,19 +87,21 @@ public void DependencyGraphToBPMN() throws BPMNModelException {
// Extract source and target
String source = parts[0];
String target = parts[1];
+ String sourceId = parts[0].replace(" ", "-").toLowerCase();
+ String targetId = parts[1].replace(" ", "-").toLowerCase();
boolean isNewTarget = false;
- BPMNElementNode sourceElement = (BPMNElementNode) process.findElementById(source);
- BPMNElementNode targetElement = (BPMNElementNode) process.findElementById(target);
+ BPMNElementNode sourceElement = (BPMNElementNode) process.findElementById(sourceId);
+ BPMNElementNode targetElement = (BPMNElementNode) process.findElementById(targetId);
// element source is not added to the process mode
if (sourceElement == null) {
// add new start event
if (startsEvent.contains(source)) {
- sourceElement = process.addEvent(source, source, BPMNTypes.START_EVENT);
+ sourceElement = process.addEvent(sourceId, source, BPMNTypes.START_EVENT);
}
// add new activity
else {
- sourceElement = process.addTask(source, source, BPMNTypes.TASK);
+ sourceElement = process.addTask(sourceId, source, BPMNTypes.TASK);
}
// TODO: Take into account the events
}
@@ -94,12 +111,12 @@ public void DependencyGraphToBPMN() throws BPMNModelException {
isNewTarget = true;
// add new start event
if (endsEvent.contains(target)) {
- targetElement = process.addEvent(target, target, BPMNTypes.END_EVENT);
+ targetElement = process.addEvent(targetId, target, BPMNTypes.END_EVENT);
splitGateway = null;
}
// add new activity
else {
- targetElement = process.addTask(target, target, BPMNTypes.TASK);
+ targetElement = process.addTask(targetId, target, BPMNTypes.TASK);
}
// TODO: Take into account the events
}
@@ -115,12 +132,12 @@ public void DependencyGraphToBPMN() throws BPMNModelException {
// call split gateway function
addSplitGateway(sourceElement, targetElement);
} else {
- if (splitGateway == null) {
- // probably there exists a loop
- if (process.isPreceding(targetElement, sourceElement)) {
- loopOperations.add(dependency);
- } else {
+ // probably there exists a loop
+ if (process.isPreceding(targetElement, sourceElement)) {
+ loopOperations.add(dependency);
+ } else {
+ if (splitGateway == null) {
// probably split
if (sourceElement.getOutgoingSequenceFlows().size() > 0) {
// call split gateway function
@@ -156,12 +173,13 @@ public void DependencyGraphToBPMN() throws BPMNModelException {
seqenceFlowId++;
}
- }
- } else {
+ } else {
- // call join gateway function
- addJoinGateway(sourceElement, targetElement);
+ // call join gateway function
+ addJoinGateway(sourceElement, targetElement);
+ }
}
+
}
}
@@ -187,6 +205,7 @@ public void DependencyGraphToBPMN() throws BPMNModelException {
private void addSplitGateway(BPMNElementNode sourceElement, BPMNElementNode targetElement)
throws BPMNModelException {
+ System.out.println();
System.out.println("SPLIT: " + sourceElement.getId() + "->" + targetElement.getId());
BPMNProcess process = model.openDefaultProces();
@@ -276,8 +295,8 @@ private boolean addNewTarget(SequenceFlow selectedSequence, Gateway selectedGate
// System.out.println("---------------GATEWAY--------------");
// System.out.println(selectedGateway.getId());
// System.out.println(selectedGateway.getType());
-// System.out.println("num="+selectedGateway.getAttribute(GATEWAY_NUM));
-// System.out.println("next="+selectedGateway.getOutgoingSequenceFlows().size());
+// System.out.println("num=" + selectedGateway.getAttribute(GATEWAY_NUM));
+// System.out.println("next=" + selectedGateway.getOutgoingSequenceFlows().size());
for (SequenceFlow currenctSequenceFlow : listSequenceFlow) {
// System.out.println("---------------NEW FLOW--------------");
BPMNElementNode successor = currenctSequenceFlow.getTargetElement();
@@ -285,8 +304,10 @@ private boolean addNewTarget(SequenceFlow selectedSequence, Gateway selectedGate
// TODO: take into account events and activities
if (successor.getType().equals(BPMNTypes.TASK) || successor.getType().equals(BPMNTypes.END_EVENT)) {
relationType = getGatewayType(successor, targetElement);
+// System.out.println((successor.getId()));
+// System.out.println((targetElement.getId()));
// System.out.println(relationType.get(GATEWAY_TYPE));
-// System.out.println("num="+relationType.get(GATEWAY_NUM));
+// System.out.println("num=" + relationType.get(GATEWAY_NUM));
if (!relationType.get(GATEWAY_NUM).contentEquals("-1")) {
allAcceptedSequenceFlows.add(currenctSequenceFlow);
if (!relationType.get(GATEWAY_NUM).contentEquals(selectedGateway.getAttribute(GATEWAY_NUM))) {
@@ -312,9 +333,9 @@ private boolean addNewTarget(SequenceFlow selectedSequence, Gateway selectedGate
// System.out.println("---------------OUTPUT--------------");
// System.out.println(selectedGateway.getId());
-// System.out.println("accepted="+acceptedSequenceFlows.size());
-// System.out.println("all="+allAcceptedSequenceFlows.size());
-// System.out.println("next="+selectedGateway.getOutgoingSequenceFlows().size());
+// System.out.println("accepted=" + acceptedSequenceFlows.size());
+// System.out.println("all=" + allAcceptedSequenceFlows.size());
+// System.out.println("next=" + selectedGateway.getOutgoingSequenceFlows().size());
if (selectedGateway.getOutgoingSequenceFlows().size() == acceptedSequenceFlows.size()) {
// probably to add new gateway before selected gateway
System.out.println("Propbably add before");
@@ -335,10 +356,13 @@ private boolean addNewTarget(SequenceFlow selectedSequence, Gateway selectedGate
} else if (acceptedSequenceFlows.size() >= 1) {
isAdded = true;
System.out.print("Add new gateway after old gateway: ");
-
+ String gatewayType = probablyRelationType.get(GATEWAY_TYPE);
+ // TODO: general gateway
+ if (gatewayType == null) {
+ gatewayType = BPMNTypes.EXCLUSIVE_GATEWAY;
+ }
// add new gateway after selected gateway
- Gateway newGateway = process.addGateway("gt-" + gatewayId.toString(), "",
- probablyRelationType.get(GATEWAY_TYPE));
+ Gateway newGateway = process.addGateway("gt-" + gatewayId.toString(), "", gatewayType);
newGateway.setAttribute(GATEWAY_NUM, probablyRelationType.get(GATEWAY_NUM));
gatewayId++;
splitGateway = newGateway;
@@ -404,6 +428,7 @@ private Map getGatewayType(BPMNElement succeesor, BPMNElement ta
private void addJoinGateway(BPMNElementNode sourceElement, BPMNElementNode targetElement)
throws BPMNModelException {
+ System.out.println();
System.out.println("JOIN: " + sourceElement.getId() + "->" + targetElement.getId());
BPMNProcess process = model.openDefaultProces();
@@ -552,7 +577,6 @@ private void addJoinGatewayRec(BPMNElementNode sourceElement, BPMNElementNode se
gatewayId++;
splitGateway = null;
-
SequenceFlow sq = targetElement.getIngoingSequenceFlows().iterator().next();
System.out.println(sq.getSourceElement().getId());
// add new sequence flow
@@ -575,229 +599,223 @@ private void addLoopGateway(BPMNElementNode sourceElement, BPMNElementNode targe
BPMNProcess process = model.openDefaultProces();
if (targetElement.getOutgoingSequenceFlows().size() > 0) {
- System.out.println("add new loop gateways");
- // add new gateway after source ( to start loop)
- Gateway sourceGateway = process.addGateway("gt-" + gatewayId.toString(), "", BPMNTypes.EXCLUSIVE_GATEWAY);
- sourceGateway.setAttribute(GATEWAY_NUM, "-2");
- gatewayId++;
-
- // get element connected to the target
- SequenceFlow sqflow = sourceElement.getOutgoingSequenceFlows().iterator().next();
- process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceGateway.getId(),
- sqflow.getTargetElement().getId());
- seqenceFlowId++;
- process.deleteSequenceFlow(sqflow.getId());
-
- // add 2 new sequence flow
- process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceElement.getId(), sourceGateway.getId());
- seqenceFlowId++;
-
- // add new gateway before target ( end for loop)
- Gateway targetGateway = process.addGateway("gt-" + gatewayId.toString(), "", BPMNTypes.EXCLUSIVE_GATEWAY);
- targetGateway.setAttribute(GATEWAY_NUM, "-2");
- gatewayId++;
-
- // get element connected to the target
- sqflow = targetElement.getIngoingSequenceFlows().iterator().next();
- process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sqflow.getSourceElement().getId(),
- targetGateway.getId());
- seqenceFlowId++;
- process.deleteSequenceFlow(sqflow.getId());
- // add new sequence flow
- process.addSequenceFlow("sq-" + seqenceFlowId.toString(), targetGateway.getId(), targetElement.getId());
- seqenceFlowId++;
-
- // connect the gateways
- process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceGateway.getId(), targetGateway.getId());
- seqenceFlowId++;
-
- splitGateway = null;
- }
- }
-
- // old JOIND CODE
-// private void addJoinGatewayOld(BPMNElementNode sourceElement, BPMNElementNode targetElement)
-// throws BPMNModelException {
-// System.out.println("JOIN: " + sourceElement.getId() + "->" + targetElement.getId());
-// BPMNProcess process = model.openDefaultProces();
+// System.out.println("add new loop gateways");
+// // add new gateway after source ( to start loop)
+// Gateway sourceGateway = process.addGateway("gt-" + gatewayId.toString(), "", BPMNTypes.EXCLUSIVE_GATEWAY);
+// sourceGateway.setAttribute(GATEWAY_NUM, "-2");
+// gatewayId++;
//
-// // There is a loop
-// if (splitGateway == null) {
-//// throw new BPMNInvalidIDException("LOOP IS NOT SUPPORTED CURRENTLY");
-//// TODO: I should work within the is procesor in the to make sure that I'm not get the target more than time (after get the children)
-////
-//// if (targetElement.getOutgoingSequenceFlows().size() > 0) {
-////
-//// System.out.println("add new loop gateways");
-//// // add new gateway after source ( to start loop)
-//// Gateway sourceGateway = process.addGateway("gt-" + gatewayId.toString(), "", BPMNTypes.EXCLUSIVE_GATEWAY);
-//// sourceGateway.setAttribute(GATEWAY_NUM, "-1");
-//// gatewayId++;
-////
-//// // add 2 new sequence flow
-//// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceElement.getId(), sourceGateway.getId());
-//// seqenceFlowId++;
-////
-////
-////
-//// // add new gateway before target ( end for loop)
-//// Gateway targetGateway = process.addGateway("gt-" + gatewayId.toString(), "", BPMNTypes.EXCLUSIVE_GATEWAY);
-//// targetGateway.setAttribute(GATEWAY_NUM, "-1");
-//// gatewayId++;
-////
-////
-//// // get element connected to the target
-//// SequenceFlow sqflow = targetElement.getIngoingSequenceFlows().iterator().next();
-//// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sqflow.getSourceElement().getId(), targetGateway.getId());
-//// seqenceFlowId++;
-//// process.deleteSequenceFlow(sqflow.getId());
-//// // add new sequence flow
-//// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), targetGateway.getId(),
-//// targetElement.getId());
-//// seqenceFlowId++;
-////
-//// //connect the gateways
-//// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceGateway.getId(), targetGateway.getId());
-//// seqenceFlowId++;
-////
-////
-//// splitGateway = null;
-//// }
-// } else {
-// // get target sequence flow
-// SequenceFlow sq = targetElement.getIngoingSequenceFlows().iterator().next();
-// // get succeed
-// BPMNElementNode proceesorElement = sq.getSourceElement();
-// if (!targetElement.getType().contentEquals(BPMNTypes.TASK)) {
-// proceesorElement = targetElement;
-// }
-// // check if it is activity
-// // TODO: look at the type
-// if (proceesorElement.getType().contentEquals(BPMNTypes.TASK)) {
+// // get element connected to the target
+// SequenceFlow sqflow = sourceElement.getOutgoingSequenceFlows().iterator().next();
+// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceGateway.getId(),
+// sqflow.getTargetElement().getId());
+// seqenceFlowId++;
+// process.deleteSequenceFlow(sqflow.getId());
//
-// process.deleteSequenceFlow(sq.getId());
-// // add new gateway
+// // add 2 new sequence flow
+// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceElement.getId(), sourceGateway.getId());
+// seqenceFlowId++;
//
-// Gateway newGateway = process.addGateway("gt-" + gatewayId.toString(), "", splitGateway.getType());
-// newGateway.setAttribute(GATEWAY_NUM, splitGateway.getAttribute(GATEWAY_NUM));
-// gatewayId++;
-// splitGateway = null;
+// // add new gateway before target ( end for loop)
+// Gateway targetGateway = process.addGateway("gt-" + gatewayId.toString(), "", BPMNTypes.EXCLUSIVE_GATEWAY);
+// targetGateway.setAttribute(GATEWAY_NUM, "-2");
+// gatewayId++;
//
-// // add 3 new sequence flow
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceElement.getId(), newGateway.getId());
-// seqenceFlowId++;
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), proceesorElement.getId(), newGateway.getId());
-// seqenceFlowId++;
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), newGateway.getId(), targetElement.getId());
-// seqenceFlowId++;
-// } else {
-// // is gateway
-// Integer flowCounter = 0;
-// List acceptedSequenceFlowList = new ArrayList<>();
-// List sqflowList = proceesorElement.getIngoingSequenceFlows().stream()
-// .collect(Collectors.toList());
-// for (SequenceFlow currenctSecquenceFlow : sqflowList) {
-// BPMNElementNode currenctProcessor = currenctSecquenceFlow.getSourceElement();
-// flowCounter++;
-// if (process.isProcceding(splitGateway, currenctProcessor)) {
-// acceptedSequenceFlowList.add(currenctSecquenceFlow);
-// }
-// }
-// // add to the same gateway
-// if (acceptedSequenceFlowList.size() == flowCounter) {
-// // check if the same gateway type
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceElement.getId(),
-// proceesorElement.getId());
-// seqenceFlowId++;
-// splitGateway = null;
-// } else if (acceptedSequenceFlowList.size() > 1) {
-// // add new gateway for accepted element
-// Gateway newGateway = process.addGateway("gt-" + gatewayId.toString(), "", splitGateway.getType());
-// newGateway.setAttribute(GATEWAY_NUM, splitGateway.getAttribute(GATEWAY_NUM));
-// gatewayId++;
-// splitGateway = null;
+// // get element connected to the target
+// sqflow = targetElement.getIngoingSequenceFlows().iterator().next();
+// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sqflow.getSourceElement().getId(),
+// targetGateway.getId());
+// seqenceFlowId++;
+// process.deleteSequenceFlow(sqflow.getId());
+// // add new sequence flow
+// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), targetGateway.getId(), targetElement.getId());
+// seqenceFlowId++;
//
-// // add new sequence flow
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), proceesorElement.getId(),
-// newGateway.getId());
-// seqenceFlowId++;
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceElement.getId(),
-// newGateway.getId());
-// seqenceFlowId++;
-// for (SequenceFlow sequence : acceptedSequenceFlowList) {
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sequence.getSourceElement().getId(),
-// newGateway.getId());
-// seqenceFlowId++;
-// process.deleteSequenceFlow(sequence.getId());
-// }
-// } else if (acceptedSequenceFlowList.size() == 0) {
-// // add new gateway for connected to exsitng element
-// Gateway newGateway = process.addGateway("gt-" + gatewayId.toString(), "", splitGateway.getType());
-// newGateway.setAttribute(GATEWAY_NUM, splitGateway.getAttribute(GATEWAY_NUM));
-// gatewayId++;
-// splitGateway = null;
+// // connect the gateways
+// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceGateway.getId(), targetGateway.getId());
+// seqenceFlowId++;
//
-// // add new sequence flow
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), newGateway.getId(),
-// proceesorElement.getId());
-// seqenceFlowId++;
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceElement.getId(),
-// newGateway.getId());
-// seqenceFlowId++;
+// splitGateway = null;
+ }
+ }
+
+ public void saveMode(String path) {
+ try {
+ String output;
+ BPMNLayoutGenerator bpmnLayoutGenerator = new BPMNLayoutGenerator();
+ output = bpmnLayoutGenerator.generateLayoutFromBPMNSemantic(model.getXml(), ExportType.valueOf("BPMN"));
+
+ File outputFile = new File(path);
+ FileUtils.touch(outputFile);
+ Files.write(outputFile.toPath(), output.getBytes());
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ model.save(path);
+ System.out.println("ERROR- The model is saved without layouting");
+ e.printStackTrace();
+ }
+
+// String[] args = new String[]{
+// "-o", "C:\\Users\\AliNourEldin\\Desktop\\bpmn-layout\\bpmn-auto-layout\\test\\fixtures\\testt2.bpmn",
+// "C:\\Users\\AliNourEldin\\Desktop\\bpmn-layout\\bpmn-auto-layout\\test\\fixtures\\testt.bpmn"};
+// try {
+// App.main(args);
+// } catch (Exception e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+ logger.info("...model creation sucessful");
+ }
+
+ public static void main(String[] args) {
+ // Start timing
+ long startTime = System.nanoTime();
+// System.out.println(args.length);
+ if (args.length == 6) {
+ /**
+ * "start_a" "[end_f,end_b]" "[a->b, a->c, a->d, a->e, c->f, d->f, e->f,
+ * start_a->a, b->end_b, f->end_f]" "[[b, c, d, e], [c, d]]" "[[c, e, d]]"
+ */
+ String output = args[0];
+ String startEventString = args[1];
+ String endEventsString = args[2];
+ String dependencyRelationsString = args[3];
+ String parallelRelationString = args[4];
+ String decisionRelationString = args[5];
+
+ List startsEvents = new ArrayList<>();
+ startsEvents.add(startEventString);
+ List endEvents = stringToList(endEventsString);
+ LinkedList events = stringToList(dependencyRelationsString);
+
+ LinkedList> decisionRelations = transformStringToList(decisionRelationString);
+ System.out.println(decisionRelations);
+ System.out.println(decisionRelations.size());
+
+ LinkedList> parallelRelations = transformStringToList(parallelRelationString);
+ System.out.println(parallelRelations);
+ System.out.println(parallelRelations.size());
+
+// System.out.println(events);
+ LinkedList orderedEvents = DepthFirstSearch.DFSToList(events, startEventString);
+// System.out.println(orderedEvents);
+
+ Map>> relations = new LinkedHashMap<>();
+ relations.put(DECISION, decisionRelations);
+
+ relations.put(PARALLEL, parallelRelations);
+ System.out.println(parallelRelations);
+ try {
+ BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startsEvents, endEvents, orderedEvents, relations);
+ bpmnDiscovery.DependencyGraphToBPMN();
+ System.out.println("Finish discovery ...");
+ bpmnDiscovery.saveMode(output);
+ } catch (BPMNModelException e) {
+ e.printStackTrace();
+ }
+ } else {
+// String output = path;
+// String startEventString = "Customer brings in a defective computer";
+// String endEventsString = "[Customer takes the computer home unrepaired,Repair process is considered complete]";
+// String dependencyRelationsString = "[Customer brings in a defective computer->Bring in defective computer,Bring in defective computer->Check defect and calculate repair cost,Check defect and calculate repair cost->Present repair cost,Present repair cost->Decide on repair cost acceptability,Decide on repair cost acceptability->Check and repair hardware,Decide on repair cost acceptability->Check and configure software,Decide on repair cost acceptability->Take computer home unrepaired,Check and repair hardware->Test system functionality,Check and configure software->Test system functionality,Test system functionality->Consider repair process complete,Consider repair process complete->Repair process is considered complete,Take computer home unrepaired->Customer takes the computer home unrepaired]";
+// String parallelRelationString = "[[Check and configure software,Check and repair hardware]]";
+// String decisionRelationString = " [[Take computer home unrepaired, Check and configure software, Check and repair hardware]]";
+//
+// List startsEvents = new ArrayList<>();
+// startsEvents.add(startEventString);
+// List endEvents = stringToList(endEventsString);
+// LinkedList events = stringToList(dependencyRelationsString);
//
-// } else if (acceptedSequenceFlowList.size() == 1) {
-// proceesorElement = acceptedSequenceFlowList.get(0).getSourceElement();
-// if (proceesorElement.getType().contentEquals(BPMNTypes.TASK)) {
+// LinkedList> decisionRelations = transformStringToList(decisionRelationString);
+// System.out.println(decisionRelations);
+// System.out.println(decisionRelations.size());
//
-// process.deleteSequenceFlow(acceptedSequenceFlowList.get(0).getId());
-// // add new gateway
+// LinkedList> parallelRelations = transformStringToList(parallelRelationString);
+// System.out.println(parallelRelations);
+// System.out.println(parallelRelations.size());
//
-// Gateway newGateway = process.addGateway("gt-" + gatewayId.toString(), "",
-// splitGateway.getType());
-// newGateway.setAttribute(GATEWAY_NUM, splitGateway.getAttribute(GATEWAY_NUM));
-// gatewayId++;
-// splitGateway = null;
+//// System.out.println(events);
+// LinkedList orderedEvents = DepthFirstSearch.DFSToList(events, startEventString);
+//// System.out.println(orderedEvents);
//
-// // add 3 new sequence flow
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), sourceElement.getId(),
-// newGateway.getId());
-// seqenceFlowId++;
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), proceesorElement.getId(),
-// newGateway.getId());
-// seqenceFlowId++;
-// process.addSequenceFlow("sq-" + seqenceFlowId.toString(), newGateway.getId(),
-// acceptedSequenceFlowList.get(0).getTargetElement().getId());
-// seqenceFlowId++;
-// } else {
-// addJoinGateway(sourceElement, proceesorElement);
-// }
-// } else {
-// // repeat
-// addJoinGateway(sourceElement, proceesorElement);
-// }
+// Map>> relations = new LinkedHashMap<>();
+// relations.put(DECISION, decisionRelations);
+// relations.put(PARALLEL, parallelRelations);
+// try {
+// BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startsEvents, endEvents, orderedEvents, relations);
+// bpmnDiscovery.DependencyGraphToBPMN();
+// System.out.println("Finish discovery ...");
+// bpmnDiscovery.saveMode(output);
+// } catch (BPMNModelException e) {
+// e.printStackTrace();
// }
-// }
-// }
+ System.out.println(
+ "INVALID INPUTS: outputPath (/src/example.bpmn), startEvent, endEvents (List), dependencyRelations(List), parallelRelation (List[List]), decisionRelation(List[List])");
+ // correct
+// example1();
+ // correct
+ // example2();
+ // correct
+ // example3();
+ // correct
+ // example4();
+ // double loop
+ // example5();
+ // example6();
+ }
+ long endTime = System.nanoTime();
+ long duration = (endTime - startTime) / 1_000_000; // Convert nanoseconds to milliseco
+ System.out.println("Execution time: " + duration + " ms");
+ }
- public void saveMode() {
- model.save("C:\\Users\\AliNourEldin\\Desktop\\bpmn-layout\\bpmn-auto-layout\\test\\fixtures\\testt.bpmn");
- logger.info("...model creation sucessful");
+ private static LinkedList stringToList(String data) {
+ // Remove the surrounding square brackets
+ data = data.substring(1, data.length() - 1);
+
+ // Split the string by commas
+ String[] elements = data.split(",\\s*");
+
+ // Create a list and add the elements
+ LinkedList list = new LinkedList<>();
+ for (String element : elements) {
+ list.add(element.trim());
+ }
+ return list;
}
- public static void main(String[] args) {
- // correct
-// example1();
- // correct
-// example2();
- // correct
-// example3();
- // correct
-// example4();
- // double loop
-// example5();
- example6();
+ private static LinkedList> transformStringToList(String data) {
+ LinkedList> outerList = new LinkedList<>();
+
+ // Remove the outer brackets
+ String trimmedInput = data.substring(1, data.length() - 1);
+ if (!trimmedInput.isEmpty()) {
+ // Handle the case for a single list inside the outer brackets
+ if (trimmedInput.charAt(0) == '[') {
+ trimmedInput = trimmedInput.substring(1, trimmedInput.length() - 1);
+ }
+
+ // Split into individual list strings
+ String[] listStrings = trimmedInput.split("],\\[");
+
+ for (String listString : listStrings) {
+ // Remove any remaining brackets
+ listString = listString.replaceAll("[\\[\\]]", "");
+
+ // Split the string by commas and convert to a list
+ LinkedList innerList = new LinkedList<>(Arrays.asList(listString.split(",")));
+ innerList.replaceAll(String::trim);
+ innerList.replaceAll(s -> s.replace(" ", "-"));
+ innerList.replaceAll(String::toLowerCase);
+ // Add the inner list to the outer list
+ outerList.add(innerList);
+ }
+ }
+ return outerList;
}
+ static String path = "C:\\Users\\AliNourEldin\\Desktop\\bpmn-layout\\bpmn-auto-layout\\test\\fixtures\\testt.bpmn";
+
// the ex use to describe the algo in the confluence page
private static void example1() {
LinkedList events = new LinkedList();
@@ -817,8 +835,8 @@ private static void example1() {
events.add("f->end1");
-// events.add("a->g");
-// events.add("g->f");
+ events.add("a->g");
+ events.add("g->f");
// events.add("f->a");
String startEvent = "start";
@@ -839,7 +857,7 @@ private static void example1() {
add("c");
add("d");
add("e");
-// add("g");
+ add("g");
}
});
decisionRelations.add(new LinkedList() {
@@ -858,21 +876,15 @@ private static void example1() {
add("d");
}
});
-// parallelRelations.add(new LinkedList() {
-// {
-// add("c");
-//
-// }
-// });
Map>> relations = new LinkedHashMap<>();
relations.put(DECISION, decisionRelations);
relations.put(PARALLEL, parallelRelations);
-
try {
BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startsEvent, endEvents, orderedEvents, relations);
bpmnDiscovery.DependencyGraphToBPMN();
- bpmnDiscovery.saveMode();
+ System.out.println("Finish discovery ...");
+ bpmnDiscovery.saveMode(path);
} catch (BPMNModelException e) {
e.printStackTrace();
}
@@ -940,7 +952,7 @@ private static void example2() {
try {
BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startsEvent, endEvents, orderedEvents, relations);
bpmnDiscovery.DependencyGraphToBPMN();
- bpmnDiscovery.saveMode();
+ bpmnDiscovery.saveMode(path);
} catch (BPMNModelException e) {
e.printStackTrace();
}
@@ -1008,7 +1020,7 @@ private static void example3() {
try {
BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startsEvent, endEvents, orderedEvents, relations);
bpmnDiscovery.DependencyGraphToBPMN();
- bpmnDiscovery.saveMode();
+ bpmnDiscovery.saveMode(path);
} catch (BPMNModelException e) {
e.printStackTrace();
}
@@ -1078,7 +1090,7 @@ private static void example4() {
try {
BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startsEvent, endEvents, orderedEvents, relations);
bpmnDiscovery.DependencyGraphToBPMN();
- bpmnDiscovery.saveMode();
+ bpmnDiscovery.saveMode(path);
} catch (BPMNModelException e) {
e.printStackTrace();
}
@@ -1156,7 +1168,7 @@ private static void example5() {
try {
BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startsEvent, endEvents, orderedEvents, relations);
bpmnDiscovery.DependencyGraphToBPMN();
- bpmnDiscovery.saveMode();
+ bpmnDiscovery.saveMode(path);
} catch (BPMNModelException e) {
e.printStackTrace();
}
@@ -1231,7 +1243,7 @@ private static void example6() {
try {
BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startsEvent, endEvents, orderedEvents, relations);
bpmnDiscovery.DependencyGraphToBPMN();
- bpmnDiscovery.saveMode();
+ bpmnDiscovery.saveMode(path);
} catch (BPMNModelException e) {
e.printStackTrace();
}
diff --git a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/XESAnalyzer.java b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/XESAnalyzer.java
index 8e60989b..8a9da025 100644
--- a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/XESAnalyzer.java
+++ b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/XESAnalyzer.java
@@ -4,125 +4,117 @@
import org.deckfour.xes.model.XEvent;
import org.deckfour.xes.model.XLog;
import org.deckfour.xes.model.XTrace;
+import org.openbpmn.bpmn.discovery.model.DependencyGraph;
+import org.openbpmn.bpmn.discovery.model.DepthFirstSearch;
+import org.openbpmn.bpmn.discovery.model.RelationConverter;
import org.openbpmn.bpmn.exceptions.BPMNModelException;
+import io.process.analytics.tools.bpmn.generator.App;
+
import java.io.File;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class XESAnalyzer {
-
+
+ //TODO: error in loops!
+ // loop position=> maybe should merge the list of loop based on the target to know where we should add the loop gateway (split gateway of loop)
+ // some relations are removed when there is a loop in this treatment of data, we should take care to how I treat the data
public static void main(String[] args) {
try {
// Start timing
long startTime = System.nanoTime();
XLog log = new XESAnalyzer().readLog("C:\\Users\\AliNourEldin\\Downloads\\splitminerwg\\diagram.xes");
- Set> traces = extractTracePairs(log);
- Set dependencyGraph = generateDependencyGraph(traces);
- Set startActivities = findStartActivities(traces);
- Set endActivities = findEndActivities(traces);
-
- Map> dependencyRelations = RelationConverter
- .relationsToMap(new ArrayList(dependencyGraph));
-
- System.out.println(dependencyRelations);
- Set> loops = findLoop(dependencyRelations, traces);
- LinkedList> parallels = findParallelPairs(dependencyRelations, traces);
-
- Map> dependencyRelationsWithParrallel = RelationConverter
- .relationsToMap(new ArrayList(dependencyGraph));
-
- // remove loop
- removeLoops(dependencyRelationsWithParrallel, loops, traces);
- System.out.println(dependencyRelationsWithParrallel);
- LinkedList> decisions = findDecisions(dependencyRelationsWithParrallel, dependencyRelations,
- traces);
-
- System.out.println("Dependency Relations: " + dependencyRelations);
- System.out.println("Loop Events: " + loops);
- System.out.println("Parallel Events: " + parallels);
- System.out.println("Decisions: " + decisions);
-
- LinkedList relations = mapToListOfRelations(dependencyRelations);
- List startEvents = new ArrayList();
- List endEvents = new ArrayList();
-
- System.out.println("Relations: " + relations);
- for (String str : startActivities) {
- String startEvent = "start_" + str;
- startEvents.add(startEvent);
- relations.add(startEvent + "->" + str);
- }
-
- for (String str : endActivities) {
- String endEvent = "end_" + str;
- endEvents.add(endEvent);
- relations.add(str + "->" + endEvent);
- }
- LinkedList orderedEvents = DepthFirstSearch.DFSToList(relations, startEvents.iterator().next());
-
- Map>> relations1 = new LinkedHashMap<>();
- relations1.put(BPMNDiscovery.DECISION, convertToListOfLists(decisions));
- relations1.put(BPMNDiscovery.PARALLEL, convertToListOfLists(parallels));
+ DependencyGraph dependencyGraph = generateDependencyGraph(log);
+ dependencyGraph.findLoopsAndParrallelism();
+ System.out.println(dependencyGraph.getLoops());
+ System.out.println(dependencyGraph.parallelism);
+ System.out.println(dependencyGraph.getParallelims());
+ System.out.println(dependencyGraph.getDecisions());
+
+//
+//
+//
+// Set startActivities = findStartActivities(traces);
+// Set endActivities = findEndActivities(traces);
+//
+//
+// Map> dependencyRelations = RelationConverter
+// .relationsToMap(new ArrayList(dependencyGraph));
+// System.out.println(dependencyRelations);
+//
+// Set> loops = findLoop(dependencyRelations, traces);
+// System.out.println("After:");
+// System.out.println(dependencyRelations);
+//
+// Set> parallels = findParallelPairs(dependencyRelations, traces);
+//
+// // remove loop
+//// removeLoops(dependencyRelations, loops, traces);
+//
+// LinkedList> decisions = findDecisions(parallels, dependencyRelations);
+//
+// LinkedList> mergeParralelism = mergeParallelism(parallels,dependencyRelations, traces);
+// System.out.println("Dependency Relations: " + dependencyRelations);
+// System.out.println("Loop Events: " + loops);
+// System.out.println("Parallel Events: " + mergeParralelism);
+// System.out.println("Decisions: " + decisions);
+//
+// LinkedList relations = mapToListOfRelations(dependencyRelations);
+// List startEvents = new ArrayList();
+// List endEvents = new ArrayList();
+//
+// System.out.println("Relations: " + relations);
+// for (String str : startActivities) {
+// String startEvent = "start_" + str;
+// startEvents.add(startEvent);
+// relations.add(startEvent + "->" + str);
+// }
+//
+// for (String str : endActivities) {
+// String endEvent = "end_" + str;
+// endEvents.add(endEvent);
+// relations.add(str + "->" + endEvent);
+// }
+// System.out.println("Dependency Relations with events: " + relations);
+// LinkedList orderedEvents = DepthFirstSearch.DFSToList(relations, startEvents.iterator().next());
+//
+// Map>> relations1 = new LinkedHashMap<>();
+// relations1.put(BPMNDiscovery.DECISION, convertToListOfLists(decisions));
+// relations1.put(BPMNDiscovery.PARALLEL, convertToListOfLists(mergeParralelism));
// try {
// BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startEvents, endEvents, orderedEvents, relations1);
// bpmnDiscovery.DependencyGraphToBPMN();
-// bpmnDiscovery.saveMode();
+// long endTime = System.nanoTime();
+// long duration = (endTime - startTime) / 1_000_000; // Convert nanoseconds to milliseco
+// System.out.println("Execution time: " + duration + " ms");
+// bpmnDiscovery.saveMode("C:\\Users\\AliNourEldin\\Desktop\\bpmn-layout\\bpmn-auto-layout\\test\\fixtures\\testt.bpmn");
+//
// } catch (BPMNModelException e) {
// e.printStackTrace();
// }
- // Stop timing
- long endTime = System.nanoTime();
- // Calculate execution time in milliseconds
- long duration = (endTime - startTime) / 1_000_000; // Convert nanoseconds to milliseconds
-
- System.out.println("Execution time: " + duration + " ms");
+// // Stop timing
+// long endTime = System.nanoTime();
+// // Calculate execution time in milliseconds
+// long duration = (endTime - startTime) / 1_000_000; // Convert nanoseconds to milliseconds
+//
+// System.out.println("Execution time: " + duration + " ms");
} catch (Exception e) {
e.printStackTrace();
}
}
- static void removeLoops(Map> dependencyRelationsWithParrallel, Set> loops,
+ static void removeLoops(Map> dependencyRelations, Set> loops,
Set> traces) {
for (Set pair : loops) {
// self-loop
if (pair.size() == 1) {
Iterator iter = pair.iterator();
String first = iter.next();
- dependencyRelationsWithParrallel.get(first).remove(first);
+ dependencyRelations.get(first).remove(first);
continue;
}
- Iterator iter = pair.iterator();
- String first = iter.next();
- String second = iter.next();
-
- boolean isFirstElementBeforeSecond = true;
- for (List trace : traces) {
- if (trace.indexOf(first) > trace.indexOf(second)) {
- isFirstElementBeforeSecond = false;
- break;
- }
-
- }
- String source;
- String target;
- if (isFirstElementBeforeSecond) {
- source = first;
- target = second;
- } else {
- source = second;
- target = first;
- }
- for (Map.Entry> relations : dependencyRelationsWithParrallel.entrySet()) {
- if (!relations.getKey().contentEquals(source)) {
- relations.getValue().remove(target);
- }
- }
- dependencyRelationsWithParrallel.get(target).removeAll(dependencyRelationsWithParrallel.get(source));
-
- //TODO: remove source from others base on the order of the sources of this source
-
}
}
@@ -135,16 +127,7 @@ public static LinkedList> convertToListOfLists(LinkedList mapToListOfRelations(Map> dependencies) {
- LinkedList relations = new LinkedList<>();
- for (Map.Entry> entry : dependencies.entrySet()) {
- String key = entry.getKey();
- for (String value : entry.getValue()) {
- relations.add(key + "->" + value);
- }
- }
- return relations;
- }
+
public XLog readLog(String filePath) throws Exception {
XesXmlParser parser = new XesXmlParser();
@@ -157,66 +140,30 @@ public XLog readLog(String filePath) throws Exception {
throw new Exception("Unable to parse log");
}
- public static Set generateDependencyGraph(Set> traces) {
- Map> dependencies = new HashMap<>();
-
- // Process each trace
- for (List trace : traces) {
- for (int i = 0; i < trace.size() - 1; i++) {
- String source = trace.get(i);
- String target = trace.get(i + 1);
-
- // If the source is not already a key in the map, add it with a new HashSet
- dependencies.putIfAbsent(source, new HashSet<>());
- // Add the target to the set of dependencies for the source
- dependencies.get(source).add(target);
- }
- }
-
- // Convert the dependency map to a set of strings "source->target"
- Set dependencyGraph = new HashSet<>();
- for (Map.Entry> entry : dependencies.entrySet()) {
- String source = entry.getKey();
- for (String target : entry.getValue()) {
- dependencyGraph.add(source + "->" + target);
- }
- }
-
- return dependencyGraph;
- }
-
- public static Set> extractTracePairs(XLog log) {
- Set> tracePairs = new HashSet<>();
+ public static DependencyGraph generateDependencyGraph(XLog log) {
+ DependencyGraph graph = new DependencyGraph();
for (XTrace trace : log) {
- List events = new ArrayList<>();
+ String lastEvent = null;
for (XEvent event : trace) {
String eventName = event.getAttributes().get("concept:name").toString();
- events.add(eventName);
- }
-// Collections.sort(events); // Ensure the same ordering for comparison
- tracePairs.add(new ArrayList<>(events)); // Add a copy for immutability concerns
- }
- return tracePairs;
- }
-
- public static Map> convertToMap(Set> tracePairs) {
- Map> dependencyRelations = new HashMap<>();
- for (List pair : tracePairs) {
- for (int i = 0; i < pair.size(); i++) {
- String source = pair.get(i);
- dependencyRelations.putIfAbsent(source, new HashSet<>());
- for (int j = 0; j < pair.size(); j++) {
- if (i != j) {
- dependencyRelations.get(source).add(pair.get(j));
- }
+ graph.addVertex(eventName);
+
+ if(lastEvent!=null) {
+ graph.addEdge(eventName, lastEvent);
}
+ lastEvent = eventName;
}
+ graph.startActivities.add(trace.get(0).getAttributes().get("concept:name").toString());
+ graph.endActivities.add(trace.get(trace.size()-1).getAttributes().get("concept:name").toString());
}
- return dependencyRelations;
+ return graph;
}
- public static LinkedList> findDecisions(Map> dependencyRelationsWithParralellism,
- Map> dependencyRelations, Set> traces) {
+
+
+
+ public static LinkedList> findDecisions(Set> paralellism,
+ Map> dependencyRelations) {
// get pair decisions
Set> pairDecisionPoints = new HashSet<>();
@@ -224,29 +171,18 @@ public static LinkedList> findDecisions(Map> de
List targets = entry.getValue();
if (targets.size() > 1) {
for (int i = 0; i < targets.size() - 1; i++) {
-
for (int j = i + 1; j < targets.size(); j++) {
Set compatible = new HashSet<>();
String element1 = targets.get(i);
String element2 = targets.get(j);
- boolean isLoop = false;
- // checl if there exists a loop
- if (dependencyRelationsWithParralellism.containsKey(element1)
- && dependencyRelationsWithParralellism.get(element1).contains(element2)) {
- if (dependencyRelationsWithParralellism.containsKey(element2)
- && dependencyRelationsWithParralellism.get(element2).contains(element1)) {
- isLoop = true;
- }
- }
- if (isLoop == false) {
+ if (!paralellism.stream()
+ .anyMatch(sublist -> sublist.contains(element1) && sublist.contains(element2))) {
compatible.add(element1);
compatible.add(element2);
pairDecisionPoints.add(compatible);
}
-
}
-
}
}
}
@@ -285,7 +221,7 @@ public static LinkedList> findDecisions(Map> de
}
}
}
-
+ System.out.println(sortedBySource);
LinkedList> finalDecisionList = new LinkedList();
// get most frequent element
@@ -349,7 +285,8 @@ public static Set> findLoop(Map> dependencyRela
loopPairs.add(pair);
Iterator iter = pair.iterator();
String first = iter.next();
- dependencyRelations.get(first).remove(first);
+ // remove loop
+// dependencyRelations.get(first).remove(first);
continue;
}
Iterator iter = pair.iterator();
@@ -384,17 +321,19 @@ public static Set> findLoop(Map> dependencyRela
}
for (Map.Entry> relations : dependencyRelations.entrySet()) {
if (!relations.getKey().contentEquals(source)) {
- relations.getValue().remove(target);
+ // remove loop
+// relations.getValue().remove(target);
}
}
- dependencyRelations.get(target).removeAll(dependencyRelations.get(source));
+ // remove loop
+// dependencyRelations.get(target).removeAll(dependencyRelations.get(source));
}
}
return loopPairs;
}
- public static LinkedList> findParallelPairs(Map> dependencyRelations,
+ public static Set> findParallelPairs(Map> dependencyRelations,
Set> traces) {
Set> parallelPairs = new HashSet<>();
@@ -423,102 +362,7 @@ public static LinkedList> findParallelPairs(Map
removeParallelismFromDependecies(parallelPairs, dependencyRelations);
- // get all sources
- Map>> sortedBySource = new HashMap<>();
- for (Set pair : parallelPairs.stream().collect(Collectors.toList())) {
- for (String activity : pair.stream().collect(Collectors.toList())) {
- for (Map.Entry> relation : dependencyRelations.entrySet()) {
- if (relation.getValue().contains(activity)) {
- String sourceElement = relation.getKey();
- sortedBySource.putIfAbsent(sourceElement, new HashSet());
-
- }
- }
- }
- }
-
- // get sources connections
- for (Map.Entry>> source : sortedBySource.entrySet()) {
- for (Set pair : parallelPairs.stream().collect(Collectors.toList())) {
- boolean isAllSameSource = true;
- for (String activity : pair.stream().collect(Collectors.toList())) {
- if (!dependencyRelations.get(source.getKey()).contains(activity)) {
- isAllSameSource = false;
- }
- }
- if (isAllSameSource) {
- source.getValue().add(new HashSet(pair));
- }
- }
- }
-
- // Step 2 & 3: Calculate frequency and identify the highest frequency element
- String frequentElement = "";
- Set> maxFreqElements = new HashSet();
- for (Map.Entry>> entry : sortedBySource.entrySet()) {
- if (frequentElement.isEmpty()) {
- frequentElement = entry.getKey();
- maxFreqElements = entry.getValue();
- } else {
- if (entry.getValue().size() > maxFreqElements.size()) {
- frequentElement = entry.getKey();
- maxFreqElements = entry.getValue();
- }
- }
-
- }
- // Step 4: Remove elements with the highest frequency from all entries (not
- // included highest one)
- for (Map.Entry>> entry : sortedBySource.entrySet()) {
- if (!frequentElement.contentEquals(entry.getKey())) {
- Iterator> sublistIter = entry.getValue().iterator();
- while (sublistIter.hasNext()) {
- Set sublist = sublistIter.next();
- if (maxFreqElements.contains(sublist)) {
- sublistIter.remove();
- }
- }
- }
- }
-
- // union
- LinkedList> finalParalellList = new LinkedList();
- for (Map.Entry>> source : sortedBySource.entrySet()) {
- finalParalellList.addAll(unionSetsWithCommonElements(new ArrayList(source.getValue())));
- }
-
- return finalParalellList;
- }
-
- public static List> unionSetsWithCommonElements(List> originalSets) {
- boolean mergeOccurred;
-
- do {
- mergeOccurred = false;
- List> newSets = new ArrayList<>();
-
- while (!originalSets.isEmpty()) {
- Set current = originalSets.remove(0);
- int i = 0;
- // Find the first set that intersects with the current set
- while (i < newSets.size()) {
- if (!Collections.disjoint(newSets.get(i), current)) {
- newSets.get(i).addAll(current); // Merge the current set with the matching set
- mergeOccurred = true;
- break;
- }
- i++;
- }
-
- // If no intersecting set was found, add the current set as a new set
- if (i == newSets.size()) {
- newSets.add(current);
- }
- }
-
- originalSets = newSets; // Update the list for the next iteration
- } while (mergeOccurred);
- return originalSets;
+ return parallelPairs;
}
private static void removeParallelismFromDependecies(Set> parallelPairs,
diff --git a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/XESAnalyzer2.java b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/XESAnalyzer2.java
new file mode 100644
index 00000000..2411d68d
--- /dev/null
+++ b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/XESAnalyzer2.java
@@ -0,0 +1,581 @@
+package org.openbpmn.bpmn.discovery;
+
+import org.deckfour.xes.in.XesXmlParser;
+import org.deckfour.xes.model.XEvent;
+import org.deckfour.xes.model.XLog;
+import org.deckfour.xes.model.XTrace;
+import org.openbpmn.bpmn.discovery.model.DepthFirstSearch;
+import org.openbpmn.bpmn.discovery.model.RelationConverter;
+import org.openbpmn.bpmn.exceptions.BPMNModelException;
+
+import io.process.analytics.tools.bpmn.generator.App;
+
+import java.io.File;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
+public class XESAnalyzer2 {
+
+ //TODO: error in loops!
+ // loop position=> maybe should merge the list of loop based on the target to know where we should add the loop gateway (split gateway of loop)
+ // some relations are removed when there is a loop in this treatment of data, we should take care to how I treat the data
+ public static void main(String[] args) {
+ try {
+ // Start timing
+ long startTime = System.nanoTime();
+ XLog log = new XESAnalyzer2().readLog("C:\\Users\\AliNourEldin\\Downloads\\splitminerwg\\diagram.xes");
+ Set> traces = extractTracePairs(log);
+ Set dependencyGraph = generateDependencyGraph(traces);
+ Set startActivities = findStartActivities(traces);
+ Set endActivities = findEndActivities(traces);
+
+
+ Map> dependencyRelations = RelationConverter
+ .relationsToMap(new ArrayList(dependencyGraph));
+ System.out.println(dependencyRelations);
+
+ Set> loops = findLoop(dependencyRelations, traces);
+ System.out.println("After:");
+ System.out.println(dependencyRelations);
+
+ Set> parallels = findParallelPairs(dependencyRelations, traces);
+
+ // remove loop
+// removeLoops(dependencyRelations, loops, traces);
+
+ LinkedList> decisions = findDecisions(parallels, dependencyRelations);
+
+ LinkedList> mergeParralelism = mergeParallelism(parallels,dependencyRelations, traces);
+ System.out.println("Dependency Relations: " + dependencyRelations);
+ System.out.println("Loop Events: " + loops);
+ System.out.println("Parallel Events: " + mergeParralelism);
+ System.out.println("Decisions: " + decisions);
+
+ LinkedList relations = mapToListOfRelations(dependencyRelations);
+ List startEvents = new ArrayList();
+ List endEvents = new ArrayList();
+
+ System.out.println("Relations: " + relations);
+ for (String str : startActivities) {
+ String startEvent = "start_" + str;
+ startEvents.add(startEvent);
+ relations.add(startEvent + "->" + str);
+ }
+
+ for (String str : endActivities) {
+ String endEvent = "end_" + str;
+ endEvents.add(endEvent);
+ relations.add(str + "->" + endEvent);
+ }
+ System.out.println("Dependency Relations with events: " + relations);
+ LinkedList orderedEvents = DepthFirstSearch.DFSToList(relations, startEvents.iterator().next());
+
+ Map>> relations1 = new LinkedHashMap<>();
+ relations1.put(BPMNDiscovery.DECISION, convertToListOfLists(decisions));
+ relations1.put(BPMNDiscovery.PARALLEL, convertToListOfLists(mergeParralelism));
+ try {
+ BPMNDiscovery bpmnDiscovery = new BPMNDiscovery(startEvents, endEvents, orderedEvents, relations1);
+ bpmnDiscovery.DependencyGraphToBPMN();
+ long endTime = System.nanoTime();
+ long duration = (endTime - startTime) / 1_000_000; // Convert nanoseconds to milliseco
+ System.out.println("Execution time: " + duration + " ms");
+ bpmnDiscovery.saveMode("C:\\Users\\AliNourEldin\\Desktop\\bpmn-layout\\bpmn-auto-layout\\test\\fixtures\\testt.bpmn");
+
+ } catch (BPMNModelException e) {
+ e.printStackTrace();
+ }
+ // Stop timing
+ long endTime = System.nanoTime();
+ // Calculate execution time in milliseconds
+ long duration = (endTime - startTime) / 1_000_000; // Convert nanoseconds to milliseconds
+
+ System.out.println("Execution time: " + duration + " ms");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ static void removeLoops(Map> dependencyRelations, Set> loops,
+ Set> traces) {
+ for (Set pair : loops) {
+ // self-loop
+ if (pair.size() == 1) {
+ Iterator iter = pair.iterator();
+ String first = iter.next();
+ dependencyRelations.get(first).remove(first);
+ continue;
+ }
+ }
+ }
+
+ public static LinkedList> convertToListOfLists(LinkedList> listOfSets) {
+ LinkedList> resultList = new LinkedList<>();
+ for (Set set : listOfSets) {
+ LinkedList newList = new LinkedList<>(set); // Convert Set to LinkedList
+ resultList.add(newList);
+ }
+ return resultList;
+ }
+
+ private static LinkedList mapToListOfRelations(Map> dependencies) {
+ LinkedList relations = new LinkedList<>();
+ for (Map.Entry> entry : dependencies.entrySet()) {
+ String key = entry.getKey();
+ for (String value : entry.getValue()) {
+ relations.add(key + "->" + value);
+ }
+ }
+ return relations;
+ }
+
+ public XLog readLog(String filePath) throws Exception {
+ XesXmlParser parser = new XesXmlParser();
+ if (parser.canParse(new File(filePath))) {
+ List logs = parser.parse(new File(filePath));
+ if (!logs.isEmpty()) {
+ return logs.get(0); // Return the first log
+ }
+ }
+ throw new Exception("Unable to parse log");
+ }
+
+ public static Set generateDependencyGraph(Set> traces) {
+ Map> dependencies = new HashMap<>();
+
+ // Process each trace
+ for (List trace : traces) {
+ for (int i = 0; i < trace.size() - 1; i++) {
+ String source = trace.get(i);
+ String target = trace.get(i + 1);
+
+ // If the source is not already a key in the map, add it with a new HashSet
+ dependencies.putIfAbsent(source, new HashSet<>());
+ // Add the target to the set of dependencies for the source
+ dependencies.get(source).add(target);
+ }
+ }
+
+ // Convert the dependency map to a set of strings "source->target"
+ Set dependencyGraph = new HashSet<>();
+ for (Map.Entry> entry : dependencies.entrySet()) {
+ String source = entry.getKey();
+ for (String target : entry.getValue()) {
+ dependencyGraph.add(source + "->" + target);
+ }
+ }
+
+ return dependencyGraph;
+ }
+
+ public static Set> extractTracePairs(XLog log) {
+ Set> tracePairs = new HashSet<>();
+ for (XTrace trace : log) {
+ List events = new ArrayList<>();
+ for (XEvent event : trace) {
+ String eventName = event.getAttributes().get("concept:name").toString();
+ events.add(eventName);
+ }
+// Collections.sort(events); // Ensure the same ordering for comparison
+ tracePairs.add(new ArrayList<>(events)); // Add a copy for immutability concerns
+ }
+ return tracePairs;
+ }
+
+ public static Map> convertToMap(Set> tracePairs) {
+ Map> dependencyRelations = new HashMap<>();
+ for (List pair : tracePairs) {
+ for (int i = 0; i < pair.size(); i++) {
+ String source = pair.get(i);
+ dependencyRelations.putIfAbsent(source, new HashSet<>());
+ for (int j = 0; j < pair.size(); j++) {
+ if (i != j) {
+ dependencyRelations.get(source).add(pair.get(j));
+ }
+ }
+ }
+ }
+ return dependencyRelations;
+ }
+
+ public static LinkedList> findDecisions(Set> paralellism,
+ Map> dependencyRelations) {
+
+ // get pair decisions
+ Set> pairDecisionPoints = new HashSet<>();
+ for (Map.Entry> entry : dependencyRelations.entrySet()) {
+ List targets = entry.getValue();
+ if (targets.size() > 1) {
+ for (int i = 0; i < targets.size() - 1; i++) {
+ for (int j = i + 1; j < targets.size(); j++) {
+ Set compatible = new HashSet<>();
+ String element1 = targets.get(i);
+ String element2 = targets.get(j);
+
+ if (!paralellism.stream()
+ .anyMatch(sublist -> sublist.contains(element1) && sublist.contains(element2))) {
+ compatible.add(element1);
+ compatible.add(element2);
+ pairDecisionPoints.add(compatible);
+ }
+ }
+ }
+ }
+ }
+
+ // get all sources
+ Map>> sortedBySource = new HashMap<>();
+ for (Set pair : pairDecisionPoints.stream().collect(Collectors.toList())) {
+ for (String element : pair.stream().collect(Collectors.toList())) {
+ for (Map.Entry> entry : dependencyRelations.entrySet()) {
+ if (entry.getValue().contains(element)) {
+ String sourceElement = entry.getKey();
+ if (sortedBySource.containsKey(sourceElement)) {
+ if (!sortedBySource.get(sourceElement).containsKey(element)) {
+ sortedBySource.get(sourceElement).putIfAbsent(element, new HashSet());
+ }
+ } else {
+ sortedBySource.putIfAbsent(entry.getKey(), new HashMap());
+ }
+ }
+ }
+ }
+ }
+
+ // fill activity in the sources
+ for (Map.Entry>> source : sortedBySource.entrySet()) {
+ for (Map.Entry> sourceConnections : source.getValue().entrySet()) {
+ for (Set pair : pairDecisionPoints.stream().collect(Collectors.toList())) {
+ if (pair.contains(sourceConnections.getKey())) {
+ for (String activity : pair.stream().collect(Collectors.toList())) {
+ if (dependencyRelations.get(source.getKey()).contains(activity)
+ && !activity.contentEquals(sourceConnections.getKey())) {
+ sourceConnections.getValue().add(activity);
+ }
+ }
+ }
+ }
+ }
+ }
+ System.out.println(sortedBySource);
+ LinkedList> finalDecisionList = new LinkedList();
+
+ // get most frequent element
+ for (Map.Entry>> source : sortedBySource.entrySet()) {
+ while (!source.getValue().isEmpty()) {
+ String frequentElement = "";
+ Set maxFreqElements = new HashSet();
+ Map> sourceValue = source.getValue();
+ for (Entry> entry : sourceValue.entrySet()) {
+ if (frequentElement.isEmpty()) {
+ frequentElement = entry.getKey();
+ maxFreqElements = entry.getValue();
+ } else {
+ if (entry.getValue().size() > maxFreqElements.size()) {
+ frequentElement = entry.getKey();
+ maxFreqElements = entry.getValue();
+ }
+ }
+ }
+ if (maxFreqElements.size() > 0) {
+ maxFreqElements.add(frequentElement);
+ finalDecisionList.add(maxFreqElements);
+ source.getValue().remove(frequentElement);
+ for (Entry> entry : sourceValue.entrySet()) {
+ entry.getValue().remove(frequentElement);
+ }
+ } else {
+ source.getValue().remove(frequentElement);
+ }
+ }
+ }
+ return finalDecisionList;
+ }
+
+ public static Set> findLoop(Map> dependencyRelations, Set> traces) {
+ Set> paralellPairs = new HashSet<>();
+
+ // Check for mutual follows, and frequency >1
+ for (Map.Entry> source : dependencyRelations.entrySet()) {
+ String sourceActivity = source.getKey();
+ for (String follower : source.getValue()) {
+ // Check if follower also directly follows the current event
+ if (dependencyRelations.get(follower) != null
+ && dependencyRelations.get(follower).contains(sourceActivity)) {
+ // check if sourceAcitivity and follower has the same source
+ if (dependencyRelations.values().stream()
+ .anyMatch(sublist -> sublist.contains(follower) && sublist.contains(sourceActivity))) {
+ Set pair = new TreeSet<>(); // Using TreeSet to keep the order consistent
+ pair.add(sourceActivity);
+ pair.add(follower);
+ paralellPairs.add(pair);
+ }
+ }
+ }
+ }
+
+ Set> loopPairs = new HashSet<>();
+ for (Set pair : paralellPairs) {
+ // self-loop
+ if (pair.size() == 1) {
+ loopPairs.add(pair);
+ Iterator iter = pair.iterator();
+ String first = iter.next();
+ // remove loop
+// dependencyRelations.get(first).remove(first);
+ continue;
+ }
+ Iterator iter = pair.iterator();
+ String first = iter.next();
+ String second = iter.next();
+ boolean isLoop = false;
+ for (List trace : traces) {
+ if (trace.stream().filter(event -> event.contentEquals(first)).count() > 1
+ && trace.stream().filter(event -> event.contentEquals(second)).count() > 1) {
+ isLoop = true;
+ loopPairs.add(pair);
+ break;
+ }
+ }
+ if (isLoop) {
+ boolean isFirstElementBeforeSecond = true;
+ for (List trace : traces) {
+ if (trace.indexOf(first) > trace.indexOf(second)) {
+ isFirstElementBeforeSecond = false;
+ break;
+ }
+
+ }
+ String source;
+ String target;
+ if (isFirstElementBeforeSecond) {
+ source = first;
+ target = second;
+ } else {
+ source = second;
+ target = first;
+ }
+ for (Map.Entry> relations : dependencyRelations.entrySet()) {
+ if (!relations.getKey().contentEquals(source)) {
+ // remove loop
+// relations.getValue().remove(target);
+ }
+ }
+ // remove loop
+// dependencyRelations.get(target).removeAll(dependencyRelations.get(source));
+ }
+ }
+
+ return loopPairs;
+ }
+
+ public static Set> findParallelPairs(Map> dependencyRelations,
+ Set> traces) {
+ Set> parallelPairs = new HashSet<>();
+
+ // Check for mutual follows, indicating parallelism
+ for (Map.Entry> source : dependencyRelations.entrySet()) {
+ String sourceActivity = source.getKey();
+ for (String follower : source.getValue()) {
+ // Check if follower also directly follows the current event
+ if (dependencyRelations.get(follower) != null
+ && dependencyRelations.get(follower).contains(sourceActivity)) {
+ // check if sourceAcitivity and follower has the same source
+ if (dependencyRelations.values().stream()
+ .anyMatch(sublist -> sublist.contains(follower) && sublist.contains(sourceActivity))) {
+ // self-loop
+ if (!sourceActivity.contentEquals(follower)) {
+ Set pair = new TreeSet<>(); // Using TreeSet to keep the order consistent
+ pair.add(sourceActivity);
+ pair.add(follower);
+ parallelPairs.add(pair);
+ }
+
+ }
+ }
+ }
+ }
+
+ removeParallelismFromDependecies(parallelPairs, dependencyRelations);
+
+ return parallelPairs;
+ }
+
+ public static LinkedList> mergeParallelism(Set> parallelPairs,
+ Map> dependencyRelations, Set> traces) {
+ // get all sources
+ Map>> sortedBySource = new HashMap<>();
+ for (Set pair : parallelPairs.stream().collect(Collectors.toList())) {
+ for (String activity : pair.stream().collect(Collectors.toList())) {
+ for (Map.Entry> relation : dependencyRelations.entrySet()) {
+ if (relation.getValue().contains(activity)) {
+ String sourceElement = relation.getKey();
+ sortedBySource.putIfAbsent(sourceElement, new HashSet());
+
+ }
+ }
+ }
+ }
+
+ // get sources connections
+ for (Map.Entry>> source : sortedBySource.entrySet()) {
+ for (Set pair : parallelPairs.stream().collect(Collectors.toList())) {
+ boolean isAllSameSource = true;
+ for (String activity : pair.stream().collect(Collectors.toList())) {
+ if (!dependencyRelations.get(source.getKey()).contains(activity)) {
+ isAllSameSource = false;
+ }
+ }
+ if (isAllSameSource) {
+ source.getValue().add(new HashSet(pair));
+ }
+ }
+ }
+
+ // Step 2 & 3: Calculate frequency and identify the highest frequency element
+ String frequentElement = "";
+ Set> maxFreqElements = new HashSet();
+ for (Map.Entry>> entry : sortedBySource.entrySet()) {
+ if (frequentElement.isEmpty()) {
+ frequentElement = entry.getKey();
+ maxFreqElements = entry.getValue();
+ } else {
+ if (entry.getValue().size() > maxFreqElements.size()) {
+ frequentElement = entry.getKey();
+ maxFreqElements = entry.getValue();
+ }
+ }
+
+ }
+ // Step 4: Remove elements with the highest frequency from all entries (not
+ // included highest one)
+ for (Map.Entry>> entry : sortedBySource.entrySet()) {
+ if (!frequentElement.contentEquals(entry.getKey())) {
+ Iterator> sublistIter = entry.getValue().iterator();
+ while (sublistIter.hasNext()) {
+ Set sublist = sublistIter.next();
+ if (maxFreqElements.contains(sublist)) {
+ sublistIter.remove();
+ }
+ }
+ }
+ }
+
+ // union
+ LinkedList> finalParalellList = new LinkedList();
+ for (Map.Entry>> source : sortedBySource.entrySet()) {
+ finalParalellList.addAll(unionSetsWithCommonElements(new ArrayList(source.getValue())));
+ }
+ return finalParalellList;
+ }
+
+ public static List> unionSetsWithCommonElements(List> originalSets) {
+ boolean mergeOccurred;
+
+ do {
+ mergeOccurred = false;
+ List> newSets = new ArrayList<>();
+
+ while (!originalSets.isEmpty()) {
+ Set current = originalSets.remove(0);
+ int i = 0;
+ // Find the first set that intersects with the current set
+ while (i < newSets.size()) {
+ if (!Collections.disjoint(newSets.get(i), current)) {
+ newSets.get(i).addAll(current); // Merge the current set with the matching set
+ mergeOccurred = true;
+ break;
+ }
+ i++;
+ }
+
+ // If no intersecting set was found, add the current set as a new set
+ if (i == newSets.size()) {
+ newSets.add(current);
+ }
+ }
+
+ originalSets = newSets; // Update the list for the next iteration
+ } while (mergeOccurred);
+ return originalSets;
+ }
+
+ private static void removeParallelismFromDependecies(Set> parallelPairs,
+ Map> dependencies) {
+ System.out.println(parallelPairs);
+ Iterator> iterator = parallelPairs.iterator();
+ while (iterator.hasNext()) {
+ Set set = iterator.next();
+ Iterator iter = set.iterator();
+ String first = iter.next();
+ String second = iter.next();
+ // Remove second from the list of dependencies of first, if present
+ if (dependencies.containsKey(first) && dependencies.get(first).contains(second)) {
+ dependencies.get(first).remove(second);
+ }
+
+ // Remove first from the list of dependencies of second, if present
+ if (dependencies.containsKey(second) && dependencies.get(second).contains(first)) {
+ dependencies.get(second).remove(first);
+ }
+ }
+ }
+
+ private static boolean isInvalidPair(Set pair, Map> dependencies) {
+ Set checkSet = new HashSet<>(pair); // Create a set from the pair for easy comparison
+ int count = 0;
+ for (List depList : dependencies.values()) {
+ // Create a set from the dependency list
+ Set depSet = new HashSet<>(depList);
+ if (depSet.containsAll(checkSet)) {
+ count++;
+ }
+ }
+ if (count > 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public static Set findStartActivities(Set> traces) {
+ Set startEvents = new HashSet<>();
+ // Add the first event of each trace to the set
+ for (List trace : traces) {
+ startEvents.add(trace.get(0));
+ }
+
+ // Check if each event in the set really starts all traces it appears in
+ Set verifiedStartEvents = new HashSet<>(startEvents);
+ for (String event : startEvents) {
+ for (List trace : traces) {
+ if (!trace.get(0).equals(event)) {
+ verifiedStartEvents.remove(event);
+ break;
+ }
+ }
+ }
+
+ return verifiedStartEvents;
+ }
+
+ public static Set findEndActivities(Set> traces) {
+ Set endEvents = new HashSet<>();
+ // Add the last event of each trace to the set
+ for (List trace : traces) {
+ endEvents.add(trace.get(trace.size() - 1));
+ }
+
+// // Check if each event in the set really ends all traces it appears in
+// Set verifiedEndEvents = new HashSet<>(endEvents);
+// for (String event : endEvents) {
+// for (List trace : traces) {
+// if (!trace.get(trace.size() - 1).equals(event)) {
+// verifiedEndEvents.remove(event);
+// break;
+// }
+// }
+// }
+
+ return endEvents;
+ }
+}
diff --git a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/DependencyGraph.java b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/DependencyGraph.java
new file mode 100644
index 00000000..2287819d
--- /dev/null
+++ b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/DependencyGraph.java
@@ -0,0 +1,401 @@
+package org.openbpmn.bpmn.discovery.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
+import org.jgrapht.Graph;
+import org.jgrapht.alg.cycle.CycleDetector;
+import org.jgrapht.alg.cycle.JohnsonSimpleCycles;
+import org.jgrapht.alg.cycle.StackBFSFundamentalCycleBasis;
+import org.jgrapht.alg.cycle.SzwarcfiterLauerSimpleCycles;
+import org.jgrapht.alg.cycle.TarjanSimpleCycles;
+import org.jgrapht.alg.cycle.TiernanSimpleCycles;
+import org.jgrapht.graph.DefaultEdge;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.DirectedWeightedPseudograph;
+import org.jgrapht.graph.SimpleDirectedGraph;
+import org.jgrapht.traverse.BreadthFirstIterator;
+
+public class DependencyGraph {
+ public DirectedWeightedPseudograph dependencyGraph;
+ public DirectedWeightedPseudograph dependencyGraphWithLoop;
+ public Set startActivities;
+ public Set