private static BPMNDiagram removeConnectedGateways(BPMNDiagram diagram) {
    List<Gateway> XOR = getXORGateways(diagram);
    Iterator<Flow> iterator = diagram.getFlows().iterator();

    while (iterator.hasNext()) {
      Flow flow = iterator.next();
      if (XOR.contains(flow.getSource())
          && XOR.contains(flow.getTarget())
          && isSplit(diagram, flow.getSource())
          && isSplit(diagram, flow.getTarget())) {
        if (!flow.getSource().equals(flow.getTarget())) {
          List<Flow> flows = getOutgoingFlows(diagram, flow.getTarget());
          for (Flow flow1 : flows) {
            diagram.addFlow(flow.getSource(), flow1.getTarget(), "");
          }
          diagram.removeGateway((Gateway) flow.getTarget());
          XOR = getXORGateways(diagram);
        } else {
          diagram.removeEdge(flow);
        }
        iterator = diagram.getFlows().iterator();
      }
    }

    return diagram;
  }
  private static BPMNDiagram mergeActivitiesAfterXOR(BPMNDiagram diagram) {
    boolean repeat = true;
    loops:
    while (repeat) {
      repeat = false;
      List<Gateway> gateways = getXORGateways(diagram);
      for (Gateway gateway : gateways) {
        List<Activity> activities = getOutgoingActivities(diagram, gateway);
        for (int i = 0; i < activities.size(); i++) {
          String label = activities.get(i).getLabel();
          for (int j = i + 1; j < activities.size(); j++) {
            if (activities.get(j).getLabel().equals(label)) {
              diagram =
                  mergeActivitiesAfterXOR(diagram, gateway, activities.get(i), activities.get(j));
              diagram = separateSplitFromJoin(diagram);
              diagram = removeXORtoAND(diagram);
              diagram = removeConnectedGateways(diagram);
              diagram = BPMNSimplifier.removeGatewaysUseless(diagram, diagram.getGateways());
              repeat = true;
              continue loops;
            }
          }
        }
      }
    }

    return diagram;
  }
  public static BPMNDiagram mergeDiagram(BPMNDiagram diagram) {
    diagram = separateSplitFromJoin(diagram);
    diagram = removeXORtoAND(diagram);
    diagram = removeConnectedGateways(diagram);

    int curr = diagram.getNodes().size() + diagram.getFlows().size();
    int size = 0;
    while (size != curr) {
      mergeActivitiesAfterXOR(diagram);
      mergeActivitiesBeforeXOR(diagram);
      size = curr;
      curr = diagram.getNodes().size() + diagram.getFlows().size();
    }

    return diagram;
  }
 private static List<Gateway> getANDGateways(BPMNDiagram diagram) {
   List<Gateway> gateways = new ArrayList<Gateway>();
   for (Gateway gateway : diagram.getGateways()) {
     if (gateway.getGatewayType().equals(Gateway.GatewayType.PARALLEL)) {
       gateways.add(gateway);
     }
   }
   return gateways;
 }
 private static List<Flow> getOutgoingFlows(BPMNDiagram diagram, BPMNNode bpmnNode) {
   List<Flow> flows = new ArrayList<Flow>();
   for (Flow flow : diagram.getFlows()) {
     if (flow.getSource().equals(bpmnNode)) {
       flows.add(flow);
     }
   }
   return flows;
 }
 private static List<Activity> getOutgoingActivities(BPMNDiagram diagram, BPMNNode bpmnNode) {
   List<Activity> flows = new ArrayList<Activity>();
   for (Flow flow : diagram.getFlows()) {
     if (flow.getSource().equals(bpmnNode) && flow.getTarget() instanceof Activity) {
       flows.add((Activity) flow.getTarget());
     }
   }
   return flows;
 }
 private static List<BPMNNode> getIncomingNodes(BPMNDiagram diagram, BPMNNode bpmnNode) {
   List<BPMNNode> flows = new ArrayList<BPMNNode>();
   for (Flow flow : diagram.getFlows()) {
     if (flow.getTarget().equals(bpmnNode)) {
       flows.add(flow.getSource());
     }
   }
   return flows;
 }
  private static BPMNDiagram removeXORtoAND(BPMNDiagram diagram) {
    List<Gateway> XOR = getXORGateways(diagram);
    List<Gateway> AND = getANDGateways(diagram);
    for (Gateway gateway1 : XOR) {
      for (Gateway gateway2 : AND) {
        List<Flow> flows = new ArrayList<Flow>();
        for (Flow flow : diagram.getFlows()) {
          if (flow.getSource().equals(gateway1) && flow.getTarget().equals(gateway2)) {
            flows.add(flow);
          }
        }
        if (flows.size() > 1) {
          for (int i = 1; i < flows.size(); i++) {
            diagram.removeEdge(flows.get(i));
          }
        }
      }
    }

    return diagram;
  }
  private static BPMNDiagram separateSplitFromJoin(BPMNDiagram diagram) {
    Iterator<Gateway> iterator = getXORGateways(diagram).iterator();
    while (iterator.hasNext()) {
      Gateway gateway = iterator.next();
      if (isSplit(diagram, gateway) && isJoin(diagram, gateway)) {
        List<BPMNNode> incoming = getIncomingNodes(diagram, gateway);
        List<BPMNNode> outgoing = getOutgoingNodes(diagram, gateway);

        Gateway split = diagram.addGateway("", Gateway.GatewayType.DATABASED);
        Gateway join = diagram.addGateway("", Gateway.GatewayType.DATABASED);

        for (BPMNNode node : incoming) {
          diagram.addFlow(node, join, "");
        }
        for (BPMNNode node : outgoing) {
          diagram.addFlow(split, node, "");
        }
        diagram.addFlow(join, split, "");
        diagram.removeGateway(gateway);
        iterator = getXORGateways(diagram).iterator();
      }
    }

    return diagram;
  }
  private static BPMNDiagram mergeActivitiesAfterXOR(
      BPMNDiagram diagram, Gateway gateway, Activity activity1, Activity activity2) {
    List<Flow> outgoingFlows1 = getOutgoingFlows(diagram, activity1);
    List<Flow> outgoingFlows2 = getOutgoingFlows(diagram, activity2);
    List<Flow> outgoingFlows3 = getOutgoingFlows(diagram, gateway);
    List<Flow> incomingFlows = getIncomingFlows(diagram, gateway);

    String label = activity1.getLabel();
    Activity activity = diagram.addActivity(label, false, false, false, false, false);

    Set<BPMNNode> outgoingNodes1 = new UnifiedSet<BPMNNode>();
    for (Flow flow : outgoingFlows1) {
      outgoingNodes1.add(flow.getTarget());
    }
    for (Flow flow : outgoingFlows2) {
      outgoingNodes1.add(flow.getTarget());
    }

    if (outgoingNodes1.size() > 1) {
      Gateway gateway1 = diagram.addGateway("", Gateway.GatewayType.DATABASED);

      Set<BPMNNode> outgoingNodes = new UnifiedSet<BPMNNode>();
      for (Flow flow : outgoingFlows1) {
        if (!outgoingNodes.contains(flow.getTarget())) {
          outgoingNodes.add(flow.getTarget());
          diagram.addFlow(gateway1, flow.getTarget(), "");
        }
      }

      for (Flow flow : outgoingFlows2) {
        if (!outgoingNodes.contains(flow.getTarget())) {
          outgoingNodes.add(flow.getTarget());
          diagram.addFlow(gateway1, flow.getTarget(), "");
        }
      }

      diagram.addFlow(activity, gateway1, "");
    } else if (outgoingNodes1.size() > 0) {
      diagram.addFlow(activity, outgoingNodes1.toArray(new BPMNNode[1])[0], "");
    }

    diagram.removeActivity(activity1);
    diagram.removeActivity(activity2);

    if (outgoingFlows3.size() == 2) {
      for (Flow flow : incomingFlows) {
        diagram.addFlow(flow.getSource(), activity, "");
      }
      diagram.removeGateway(gateway);
    } else {
      diagram.addFlow(gateway, activity, "");
    }
    //            if(outgoingNodes.size() == 1) {
    //                BPMNNode node = outgoingNodes.toArray(new BPMNNode[1])[0];
    //                if(node instanceof Gateway) {
    //                    Gateway gateway2 = (Gateway) node;
    //                    if(gateway2.getGatewayType().equals(Gateway.GatewayType.DATABASED)) {
    //                        List<BPMNNode> list = new ArrayList<BPMNNode>();
    //                        for(Flow flow: diagram.getFlows()) {
    //                            if(flow.getSource().equals(gateway2)) {
    //                                list.add(flow.getTarget());
    //                            }
    //                        }
    //                        for(BPMNNode node1 : list) {
    //                            diagram.addFlow(activity, node1, "");
    //                        }
    //                        diagram.removeGateway(gateway1);
    //                        diagram.removeGateway(gateway2);
    //                    }
    //                }
    //            }
    //        }

    return diagram;
  }
  public static void main(String[] args) throws Exception {

    System.out.println("This is the stand alone version of the BPMNMiner algorithm proposed in:");
    System.out.println();
    System.out.println("R. Conforti, M. Dumas, L. García-Bañuelos, and M. La Rosa.");
    System.out.println(
        "BPMN Miner: Automated discovery of BPMN process models with hierarchical structure.");
    System.out.println("Information Systems, 56, pp 284-303, 2016.");
    System.out.println();
    System.out.println("For more info contact me at [email protected]");
    System.out.println("or visit my website www.raffaeleconforti.com");

    Scanner console = new Scanner(System.in);
    System.out.println("Input file:");
    String name = console.nextLine();

    while (name.endsWith(" ")) {
      name = name.substring(0, name.length() - 1);
    }

    XFactory factory = new XFactoryMemoryImpl();
    XLog log = LogImporter.importFromFile(factory, name);

    UIPluginContext fakeContext = new FakePluginContext();
    BPMNMinerCommandLine plugin = new BPMNMinerCommandLine();

    BPMNDiagram diagram = plugin.mineBPMNModel(fakeContext, log);

    System.out.println("Do you want to structure the diagram? (y/n)");
    String token = null;
    boolean structure = true;
    while (token == null) {
      token = console.nextLine();
      if (token.equalsIgnoreCase("y")) {
        structure = true;
      } else if (token.equalsIgnoreCase("n")) {
        structure = false;
      } else {
        token = null;
        System.out.println("Accepted input Y or N");
        System.out.println("Do you want to structure the diagram? (y/n)");
      }
    }

    for (Activity activity : diagram.getActivities()) {
      if (activity.getLabel().endsWith("+complete")) {
        activity
            .getAttributeMap()
            .put(
                "ProM_Vis_attr_label",
                activity.getLabel().substring(0, activity.getLabel().indexOf("+complete")));
      }
    }

    if (structure) {
      StructuringService ss = new StructuringService();
      diagram = ss.structureDiagram(diagram);
    }

    if (name.endsWith(".xes")) name = name.substring(0, name.length() - 4);
    else if (name.endsWith(".xes.gz")) name = name.substring(0, name.length() - 7);
    else if (name.endsWith(".mxml")) name = name.substring(0, name.length() - 5);
    else if (name.endsWith(".mxml.gz")) name = name.substring(0, name.length() - 8);

    System.out.println("Output file: " + name + ".bpmn");
    UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
    UIContext context = new UIContext();
    UIPluginContext uiPluginContext = context.getMainPluginContext();
    BpmnDefinitions.BpmnDefinitionsBuilder definitionsBuilder =
        new BpmnDefinitions.BpmnDefinitionsBuilder(uiPluginContext, diagram);
    BpmnDefinitions definitions = new BpmnDefinitions("definitions", definitionsBuilder);

    StringBuilder sb = new StringBuilder();
    sb.append(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            + "<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"\n "
            + "xmlns:dc=\"http://www.omg.org/spec/DD/20100524/DC\"\n "
            + "xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\"\n "
            + "xmlns:di=\"http://www.omg.org/spec/DD/20100524/DI\"\n "
            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n "
            + "targetNamespace=\"http://www.omg.org/bpmn20\"\n "
            + "xsi:schemaLocation=\"http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd\">");

    sb.append(definitions.exportElements());
    sb.append("</definitions>");
    FileWriter fileWriter = new FileWriter(new File(name + ".bpmn"));
    fileWriter.write(sb.toString());
    fileWriter.flush();
    fileWriter.close();
  }