private List<String> directoryDiff() {
    var matchesPaths = new ArrayList<String>();

    File engineExe;
    try {
      engineExe = ExternalAppUtils.getBinDiffEngine();
    } catch (DifferException | FileNotFoundException e) {
      logger.atSevere().withCause(e).log();
      CMessageBox.showError(parentWindow, e.getMessage());

      return matchesPaths;
    }

    logger.atInfo().log(
        "Start directory diff '%s' vs '%s'", primarySourcePath, secondarySourcePath);

    String workspacePath = workspace.getWorkspaceDir().getPath();
    for (DiffPairTableData data : idbPairs) {
      var destination = String.join(File.separator, workspacePath, data.getDestinationDirectory());

      var primarySource =
          String.join(File.separator, primarySourcePath, data.getIDBLocation(), data.getIDBName());

      var secondarySource =
          String.join(
              File.separator, secondarySourcePath, data.getIDBLocation(), data.getIDBName());

      setDescription(String.format("%s vs %s", data.getIDBName(), data.getIDBName()));

      var primarySourceFile = new File(primarySource);
      var secondarySourceFile = new File(secondarySource);

      String priTargetName =
          createUniqueExportFileName(primarySourceFile, secondarySourceFile, ESide.PRIMARY);
      String secTargetName =
          createUniqueExportFileName(primarySourceFile, secondarySourceFile, ESide.SECONDARY);

      var destinationFolder = new File(destination);

      if (destinationFolder.exists()) {
        var msg =
            String.format(
                "'%s' failed. Reason: Destination folder already exists.",
                data.getDestinationDirectory());
        diffingErrorMessages.add(msg);

        continue;
      }

      if (!destinationFolder.mkdir()) {
        var msg =
            String.format(
                "'%s' failed. Reason: Destination folder cannot be created.",
                data.getDestinationDirectory());
        diffingErrorMessages.add(msg);

        continue;
      }

      // Export primary IDB
      try {
        logger.atInfo().log(
            " - Start exporting primary IDB '%s' to '%s'", primarySource, destination);

        File idaExe = ExternalAppUtils.getIdaExe(primarySourceFile);
        if (idaExe == null || !idaExe.canExecute()) {
          var msg = "Can't start disassembler. Please set correct path in the main settings first.";
          logger.atSevere().log("%s", msg);
          CMessageBox.showError(parentWindow, msg);

          deleteDestinationDirectory(destinationFolder);
          return matchesPaths;
        }

        ExportProcess.startExportProcess(
            idaExe, destinationFolder, primarySourceFile, priTargetName);

        logger.atInfo().log(
            " - Finished exporting primary IDB '%s' to '%s' successfully",
            primarySource, destination);
      } catch (BinExportException e) {
        logger.atInfo().log(
            " - Exporting primary '%s' to '%s' failed. Reason: %s",
            primarySource, destination, e.getMessage());
        var msg =
            String.format(
                "Exporting primary '%s' failed. Reason: %s", primarySource, e.getMessage());
        diffingErrorMessages.add(msg);

        deleteDestinationDirectory(destinationFolder);
        continue;
      }

      // Export secondary IDB
      try {
        logger.atInfo().log(
            " - Start exporting secondary IDB '%s' to '%s'", secondarySource, destination);

        File idaExe = ExternalAppUtils.getIdaExe(secondarySourceFile);
        if (idaExe == null || !idaExe.canExecute()) {
          var msg = "Can't start disassembler. Please set correct path in the main settings first.";
          logger.atSevere().log("%s", msg);
          CMessageBox.showError(parentWindow, msg);

          return matchesPaths;
        }

        ExportProcess.startExportProcess(
            idaExe, destinationFolder, secondarySourceFile, secTargetName);

        logger.atInfo().log(
            " - Finished exporting secondary IDB '%s' to '%s' successfully",
            secondarySource, destination);
      } catch (BinExportException e) {
        logger.atWarning().log(
            " - Exporting secondary '%s' to '%s' failed. Reason: %s",
            secondarySource, destination, e.getMessage());
        var msg =
            String.format(
                "Exporting secondary '%s' failed. Reason: %s", secondarySource, e.getMessage());
        diffingErrorMessages.add(msg);

        deleteDestinationDirectory(destinationFolder);
        continue;
      }

      // Diff
      try {
        String primaryDifferArgument =
            ExportProcess.getExportFilename(priTargetName, destinationFolder);
        String secondaryDifferArgument =
            ExportProcess.getExportFilename(secTargetName, destinationFolder);

        logger.atInfo().log(" - Start diffing '%s'", destinationFolder.getName());
        DiffProcess.startDiffProcess(
            engineExe, primaryDifferArgument, secondaryDifferArgument, destinationFolder);

        String diffBinaryPath =
            DiffProcess.getBinDiffFilename(primaryDifferArgument, secondaryDifferArgument);

        logger.atInfo().log(" - Diffing '%s' done successfully", destinationFolder.getName());

        matchesPaths.add(diffBinaryPath);
      } catch (DifferException e) {
        logger.atWarning().log(
            " - Diffing '%s' failed. Reason: %s", destinationFolder.getName(), e.getMessage());
        var msg =
            String.format(
                "Diffing '%s' failed. Reason: %s", data.getDestinationDirectory(), e.getMessage());
        diffingErrorMessages.add(msg);
      }
    }

    if (diffingErrorMessages.size() == 0) {
      logger.atInfo().log(
          "Finished Directory Diff '%s' vs '%s' successfully",
          primarySourcePath, secondarySourcePath);
    } else {
      logger.atWarning().log(
          "Finished Directory Diff '%s' vs '%s' with errors.",
          primarySourcePath, secondarySourcePath);
    }

    return matchesPaths;
  }
Directory diffing thread implementation that displays visual progress.
java/ui/src/main/java/com/google/security/zynamics/bindiff/gui/tabpanels/projecttabpanel/implementations/DirectoryDiffImplementation.java