private static boolean install(String[] patches, Path dest, UpdaterUI ui, boolean backup) {
    try {
      List<File> patchFiles = new ArrayList<>(patches.length);
      File destDir = dest.toFile();
      File backupDir = null;

      String jarName = null;
      try {
        LOG.info("Extracting patch files...");
        ui.startProcess(UpdaterUI.message("extracting.patch.files"));
        for (int i = 0; i < patches.length; i++) {
          jarName = new File(patches[i]).getName();
          LOG.info("Unpacking " + jarName);
          ui.setProgress(100 * i / patches.length);
          File patchFile = Utils.getTempFile("patch" + i);
          patchFiles.add(patchFile);
          try (ZipFile zipFile = new ZipFile(patches[i]);
               InputStream in = Utils.getEntryInputStream(zipFile, PATCH_FILE_NAME);
               OutputStream out = new BufferedOutputStream(Files.newOutputStream(patchFile.toPath()))) {
            Utils.copyStream(in, out);
          }
          ui.checkCancelled();
        }
        jarName = null;

        if (backup) {
          backupDir = Utils.getTempFile("backup");
          if (!backupDir.mkdir()) throw new IOException("Cannot create a backup directory: " + backupDir);

          LOG.info("Backing up files...");
          ui.startProcess(UpdaterUI.message("backing.up.files"));
          ui.setProgressIndeterminate();
          Utils.copyDirectory(destDir.toPath(), backupDir.toPath());
        }
      }
      catch (OperationCancelledException e) {
        LOG.log(Level.WARNING, "cancelled", e);
        return false;
      }
      catch (Throwable t) {
        LOG.log(Level.SEVERE, "prepare failed", t);
        String message = (jarName != null ? UpdaterUI.message("extract.failed", jarName, t.getClass().getSimpleName(), t.getMessage())
                                          : UpdaterUI.message("prepare.failed", t.getClass().getSimpleName(), t.getMessage())) + "\n\n" +
                         ui.bold(UpdaterUI.message("nothing.changed.retry")) + "\n\n" +
                         UpdaterUI.message("see.log", logPath);
        ui.showError(message);
        return false;
      }

      boolean completed = false, needRestore = false;
      Throwable error = null;
      try {
        for (File patchFile : patchFiles) {
          PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(patchFile, destDir, ui);

          Map<String, ValidationResult.Option> resolutions = askForResolutions(preparationResult.validationResults, ui);

          PatchFileCreator.ApplicationResult applicationResult = PatchFileCreator.apply(preparationResult, resolutions, null, ui);
          needRestore |= !applicationResult.appliedActions.isEmpty();
          if (!applicationResult.applied) {
            error = applicationResult.error;
            throw new OperationCancelledException();
          }
        }
        completed = true;
      }
      catch (OperationCancelledException e) {
        LOG.log(Level.WARNING, "cancelled", e);
      }
      catch (Throwable t) {
        LOG.log(Level.SEVERE, "batch failed", t);
        error = t;
      }

      if (error != null) {
        String message = UpdaterUI.message("apply.failed", error.getClass().getSimpleName(), error.getMessage()) + "\n\n";
        if (!needRestore) {
          message += ui.bold(UpdaterUI.message("nothing.changed.retry"));
        }
        else if (backupDir == null) {
          message += ui.bold(UpdaterUI.message("corrupted.reinstall"));
        }
        else {
          message += ui.bold(UpdaterUI.message("corrupted.reverting"));
        }
        message += "\n\n" + UpdaterUI.message("see.log", logPath);
        ui.showError(message);
      }

      ui.setDescription("");

      if (!completed && needRestore && backupDir != null) {
        LOG.info("Reverting...");
        ui.startProcess(UpdaterUI.message("reverting"));
        ui.setProgressIndeterminate();

        try {
          Utils.delete(destDir);
          try {
            LOG.info("move: " + backupDir + " -> " + destDir);
            Files.move(backupDir.toPath(), destDir.toPath());
          }
          catch (IOException e) {
            LOG.log(Level.SEVERE, "move failed", e);
            Utils.delete(destDir);
            Utils.copyDirectory(backupDir.toPath(), destDir.toPath());
          }
        }
        catch (Throwable t) {
          LOG.log(Level.SEVERE, "revert failed", t);
          String message = UpdaterUI.message("revert.failed", t.getClass().getSimpleName(), t.getMessage()) + "\n\n" +
                           ui.bold(UpdaterUI.message("corrupted.reinstall")) + "\n\n" +
                           UpdaterUI.message("see.log", logPath);
          ui.showError(message);
        }
      }

      return completed;
    }
    finally {
      try {
        cleanup(ui);
        refreshApplicationIcon(dest.toString());
      }
      catch (Throwable t) {
        LOG.log(Level.WARNING, "cleanup failed", t);
      }
    }
  }
private static boolean install(String[] patches, Path dest, UpdaterUI ui, boolean backup)