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 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 endActivities; + + public Set> loops; + public Set> parallelism; + public Set> decisions; + + public DependencyGraph() { + dependencyGraph = new DirectedWeightedPseudograph<>(DefaultWeightedEdge.class); + dependencyGraphWithLoop = new DirectedWeightedPseudograph<>(DefaultWeightedEdge.class); + startActivities = new HashSet<>(); + endActivities = new HashSet<>(); + loops = new HashSet<>(); + parallelism = new HashSet<>(); + decisions = new HashSet<>(); + } + + /** + * used during the extraction of traces from the event log + * + * @param v1 + */ + public void addVertex(String v1) { + if (!dependencyGraph.vertexSet().contains(v1)) { + dependencyGraph.addVertex(v1); + dependencyGraphWithLoop.addVertex(v1); + } + } + + /** + * used during the extraction of traces from the event log + * + * @param v1 + * @param v2 + */ + public void addEdge(String v1, String v2) { + DefaultWeightedEdge edge = dependencyGraph.getEdge(v1, v2); + if (edge == null) { + // Edge does not exist, add it with initial weight 1 + edge = dependencyGraph.addEdge(v1, v2); + dependencyGraph.setEdgeWeight(edge, 1); + DefaultWeightedEdge edge1 = dependencyGraphWithLoop.addEdge(v1, v2); + dependencyGraphWithLoop.setEdgeWeight(edge1, 1); + } else { + // Edge exists, increment its weight + dependencyGraph.setEdgeWeight(edge, dependencyGraph.getEdgeWeight(edge) + 1); + DefaultWeightedEdge edge1 = dependencyGraphWithLoop.getEdge(v1, v2); + dependencyGraphWithLoop.setEdgeWeight(edge1, dependencyGraphWithLoop.getEdgeWeight(edge1) + 1); + } + } + + public void findLoopsAndParrallelism() { + Set> tempLoop = new HashSet(); + if (dependencyGraphWithLoop != null) { + //TODO: error in loop detections + // self-loop, loops, parallelism detections + SzwarcfiterLauerSimpleCycles cycleDetector = new SzwarcfiterLauerSimpleCycles<>( + dependencyGraphWithLoop); + boolean hasCycle = cycleDetector.findSimpleCycles().size() > 0; + if (hasCycle) { + List> cyclesList = cycleDetector.findSimpleCycles(); + for (List cycle : cyclesList) { + // in case of self-loop or loop + if (cycle.size() == 1) { + tempLoop.add(new HashSet(Arrays.asList(cycle.get(0), cycle.get(0)))); + } else { + String element1 = cycle.get(0); + String element2 = cycle.get(cycle.size() - 1); + tempLoop.add(new HashSet(Arrays.asList(element2, element1))); + } + } + } + } + System.out.println(tempLoop); + // remove loops from dependency graph + dependencyGraph = (DirectedWeightedPseudograph) dependencyGraphWithLoop.clone(); + loops.stream().forEach(edge -> dependencyGraph.removeEdge(edge.get(0), edge.get(1))); + + JohnsonSimpleCycles cycleDetector = new JohnsonSimpleCycles<>( + dependencyGraphWithLoop); + boolean hasCycle = cycleDetector.findSimpleCycles().size() > 0; + if (hasCycle) { + List> cyclesList = cycleDetector.findSimpleCycles(); + for (List cycle : cyclesList) { + if (cycle.size() == 2) { + String element1 = cycle.get(0); + String element2 = cycle.get(1); + + // get source of element1 + Set incomingEdgeElement1 = dependencyGraph.incomingEdgesOf(element1); + Set sourceElement1 = new HashSet<>(); + incomingEdgeElement1.stream() + .forEach(edge -> sourceElement1.add(dependencyGraph.getEdgeSource(edge))); + + // get source of element2 + Set incomingEdgeElement2 = dependencyGraph.incomingEdgesOf(element1); + Set sourceElement2 = new HashSet<>(); + incomingEdgeElement2.stream() + .forEach(edge -> sourceElement2.add(dependencyGraph.getEdgeSource(edge))); + + sourceElement1.remove(element1); + sourceElement1.remove(element2); + sourceElement2.remove(element1); + sourceElement2.remove(element2); + // if the pairs has the same source => parallelism + Set intersection = new HashSet<>(sourceElement1); // Make a copy of set1 + intersection.retainAll(sourceElement2); // Retain only elements that are also in set2 + if (!intersection.isEmpty()) { + parallelism.add(new HashSet(Arrays.asList(cycle.get(1), cycle.get(0)))); + } + } + } + } + } + + public Set> getLoops() { + return loops; + } + + public LinkedList> getParallelims() { + return mergeParallelism(); + } + + public LinkedList> getDecisions() { + + // get pair decisions + Set> pairDecisionPoints = new HashSet<>(); + for (String activity : dependencyGraph.vertexSet()) { + Set outgoingEdgeSource = dependencyGraph.incomingEdgesOf(activity); + List targetActivities = new ArrayList(); + outgoingEdgeSource.stream().forEach(edge -> targetActivities.add(dependencyGraph.getEdgeTarget(edge))); + + if (targetActivities.size() > 1) { + for (int i = 0; i < targetActivities.size() - 1; i++) { + for (int j = i + 1; j < targetActivities.size(); j++) { + Set compatible = new HashSet<>(); + String element1 = targetActivities.get(i); + String element2 = targetActivities.get(j); + + if (!parallelism.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 activity : pair.stream().collect(Collectors.toList())) { + // get all source of activity + Set incomingEdgeActivity = dependencyGraph.incomingEdgesOf(activity); + Set sourcesActivity = new HashSet<>(); + incomingEdgeActivity.stream().forEach(edge -> sourcesActivity.add(dependencyGraph.getEdgeSource(edge))); + for (String sourceElement : sourcesActivity) { + if (sortedBySource.containsKey(sourceElement)) { + if (!sortedBySource.get(sourceElement).containsKey(activity)) { + sortedBySource.get(sourceElement).putIfAbsent(activity, new HashSet()); + } + } else { + sortedBySource.putIfAbsent(sourceElement, 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 (dependencyGraph.getEdge(source.getKey(), activity) != null + && !activity.contentEquals(sourceConnections.getKey())) { + sourceConnections.getValue().add(activity); + } + } + } + } + } + } + + 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; + } + + /** + * This function to merge all the parallel relation to be in transativity + * relation (all relation after activity after the same gateway) + * + * @return + */ + public LinkedList> mergeParallelism() { + // get all sources + Map>> sortedBySource = new HashMap<>(); + for (Set pair : parallelism.stream().collect(Collectors.toList())) { + for (String activity : pair.stream().collect(Collectors.toList())) { + // get source of activity + Set incomingEdgeActivity = dependencyGraph.incomingEdgesOf(activity); + Set sourceActivity = new HashSet<>(); + incomingEdgeActivity.stream().forEach(edge -> sourceActivity.add(dependencyGraph.getEdgeSource(edge))); + sourceActivity.stream().forEach(source -> { + sortedBySource.putIfAbsent(source, new HashSet()); + }); + } + } + + // get sources connections + for (Map.Entry>> source : sortedBySource.entrySet()) { + // get target of sources + Set outgoingEdgeSource = dependencyGraph.outgoingEdgesOf(source.getKey()); + Set targetActivities = new HashSet<>(); + outgoingEdgeSource.stream().forEach(edge -> targetActivities.add(dependencyGraph.getEdgeTarget(edge))); + for (Set pair : parallelism.stream().collect(Collectors.toList())) { + boolean isAllSameSource = true; + for (String activity : pair.stream().collect(Collectors.toList())) { + if (!targetActivities.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; + } + + /** + * This function is used to merge lists with common elements + * + * @param originalSets + * @return + */ + 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; + } + + public static void main(String[] args) { + DependencyGraph graph = new DependencyGraph(); + // Create a directed graph + + // Add vertices + graph.addVertex("a"); + graph.addVertex("b"); + graph.addVertex("c"); + graph.addVertex("d"); + + // Add edges + graph.addEdge("a", "b"); + graph.addEdge("a", "c"); + graph.addEdge("b", "c"); + graph.addEdge("c", "b"); +// graph.addEdge("c", "a"); // This creates a cycle: a -> b -> c -> a + graph.addEdge("a", "d"); // This creates a cycle +// graph.addEdge("d", "d"); // This is a self-loop + + graph.findLoopsAndParrallelism(); + System.out.println(graph.getLoops()); + System.out.println(graph.getParallelims()); + System.out.println(graph.getDecisions()); + // Detect cycles +// JohnsonSimpleCycles cycleDetector = new JohnsonSimpleCycles<>(graph); +// boolean hasCycle = cycleDetector.findSimpleCycles().size() > 0; +// System.out.println(cycleDetector.findSimpleCycles()); +// System.out.println("Theerter detected? : " + (hasCycle ? "Yes" : "No")); + + // Detect self-loops +// boolean hasLoop = hasSelfLoops(graph); +// System.out.println("Self-loop detected? : " + hasLoop); + + } + +} diff --git a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/DepthFirstSearch.java b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/DepthFirstSearch.java similarity index 98% rename from open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/DepthFirstSearch.java rename to open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/DepthFirstSearch.java index 24ff4d0a..7168ece8 100644 --- a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/DepthFirstSearch.java +++ b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/DepthFirstSearch.java @@ -1,4 +1,4 @@ -package org.openbpmn.bpmn.discovery; +package org.openbpmn.bpmn.discovery.model; import java.util.*; diff --git a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/RelationConverter.java b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/RelationConverter.java similarity index 97% rename from open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/RelationConverter.java rename to open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/RelationConverter.java index 41bacb10..727d26cc 100644 --- a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/RelationConverter.java +++ b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/discovery/model/RelationConverter.java @@ -1,4 +1,4 @@ -package org.openbpmn.bpmn.discovery; +package org.openbpmn.bpmn.discovery.model; import java.util.ArrayList; import java.util.Arrays; diff --git a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/elements/BPMNProcess.java b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/elements/BPMNProcess.java index 7485239a..6342a68e 100644 --- a/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/elements/BPMNProcess.java +++ b/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/elements/BPMNProcess.java @@ -1715,7 +1715,9 @@ public Map> getDataStoresExtensions() { public boolean isPreceding(BPMNElementNode sourceElement, BPMNElementNode targetElement) { Queue queue = new LinkedList<>(); Set visited = new HashSet<>(); - + if(sourceElement.getId().contentEquals(targetElement.getId())) { + return true; + } visited.add(targetElement); queue.add(targetElement); while (!queue.isEmpty()) { diff --git a/open-bpmn.metamodel/src/test/java/org/openbpmn/discovery/TestTransformation.java b/open-bpmn.metamodel/src/test/java/org/openbpmn/discovery/TestTransformation.java index a5c7fa52..7151b5e3 100644 --- a/open-bpmn.metamodel/src/test/java/org/openbpmn/discovery/TestTransformation.java +++ b/open-bpmn.metamodel/src/test/java/org/openbpmn/discovery/TestTransformation.java @@ -9,7 +9,7 @@ import org.openbpm.bpmn.converter.DFBPMNToProc; import org.openbpmn.bpmn.BPMNModel; import org.openbpmn.bpmn.BPMNTypes; -import org.openbpmn.bpmn.discovery.RelationConverter; +import org.openbpmn.bpmn.discovery.model.RelationConverter; import org.openbpmn.bpmn.elements.Activity; import org.openbpmn.bpmn.elements.BPMNProcess; import org.openbpmn.bpmn.elements.SequenceFlow;