@RequiresEdt
  static boolean installFromDisk(@NotNull InstalledPluginsTableModel model,
                                 @NotNull PluginEnabler pluginEnabler,
                                 @NotNull File file,
                                 @Nullable Project project,
                                 @Nullable JComponent parent,
                                 @NotNull Consumer<? super PluginInstallCallbackData> callback) {
    try {
      Path path = file.toPath();
      IdeaPluginDescriptorImpl pluginDescriptor = ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
        return PluginDescriptorLoader.loadDescriptorFromArtifact(path, null);
      }, IdeBundle.message("action.InstallFromDiskAction.progress.text"), true, project);

      if (pluginDescriptor == null) {
        MessagesEx.showErrorDialog(parent,
                                   IdeBundle.message("dialog.message.fail.to.load.plugin.descriptor.from.file", file.getName()),
                                   CommonBundle.getErrorTitle());
        return false;
      }

      if (!PluginManagerMain.checkThirdPartyPluginsAllowed(List.of(pluginDescriptor))) {
        return false;
      }

      if (!PluginManagementPolicy.getInstance().canInstallPlugin(pluginDescriptor)) {
        String message = IdeBundle.message("dialog.message.plugin.is.not.allowed", pluginDescriptor.getName());
        MessagesEx.showWarningDialog(parent, message, IdeBundle.message("dialog.title.install.plugin"));
        return false;
      }

      InstalledPluginsState ourState = InstalledPluginsState.getInstance();
      if (ourState.wasInstalled(pluginDescriptor.getPluginId())) {
        String message = IdeBundle.message("dialog.message.plugin.was.already.installed", pluginDescriptor.getName());
        MessagesEx.showWarningDialog(parent, message, IdeBundle.message("dialog.title.install.plugin"));
        return false;
      }

      PluginLoadingError error = PluginManagerCore.checkBuildNumberCompatibility(pluginDescriptor, PluginManagerCore.getBuildNumber());
      if (error != null) {
        MessagesEx.showErrorDialog(parent, error.getDetailedMessage(), CommonBundle.getErrorTitle());
        return false;
      }
      if (isBrokenPlugin(pluginDescriptor)) {
        String message =
          CoreBundle.message("plugin.loading.error.long.marked.as.broken", pluginDescriptor.getName(), pluginDescriptor.getVersion());
        MessagesEx.showErrorDialog(parent, message, CommonBundle.getErrorTitle());
        return false;
      }

      IdeaPluginDescriptor installedPlugin = PluginManagerCore.getPlugin(pluginDescriptor.getPluginId());
      if (installedPlugin != null && ApplicationInfoEx.getInstanceEx().isEssentialPlugin(installedPlugin.getPluginId())) {
        String message = IdeBundle
          .message("dialog.message.plugin.core.part", pluginDescriptor.getName(), ApplicationNamesInfo.getInstance().getFullProductName());
        MessagesEx.showErrorDialog(parent, message, CommonBundle.getErrorTitle());
        return false;
      }

      PluginManagerUsageCollector.pluginInstallationStarted(pluginDescriptor,
                                                            InstallationSourceEnum.FROM_DISK,
                                                            installedPlugin != null ? installedPlugin.getVersion() : null);

      if (!PluginSignatureChecker.verifyIfRequired(pluginDescriptor, file, false, true)) {
        return false;
      }

      Task.WithResult<Pair<PluginInstallOperation, ? extends IdeaPluginDescriptor>, RuntimeException> task =
        new Task.WithResult<>(null,
                              parent,
                              IdeBundle.message("progress.title.checking.plugin.dependencies"),
                              true) {
          @Override
          protected @NotNull Pair<PluginInstallOperation, ? extends IdeaPluginDescriptor> compute(@NotNull ProgressIndicator indicator) {
            PluginInstallOperation operation = new PluginInstallOperation(List.of(),
                                                                          CustomPluginRepositoryService.getInstance()
                                                                            .getCustomRepositoryPlugins(),
                                                                          pluginEnabler,
                                                                          indicator);
            operation.setAllowInstallWithoutRestart(true);

            return operation.checkMissingDependencies(pluginDescriptor, null) ?
                   Pair.create(operation, operation.checkDependenciesAndReplacements(pluginDescriptor)) :
                   Pair.empty();
          }
        };

      Pair<PluginInstallOperation, ? extends IdeaPluginDescriptor> pair = ProgressManager.getInstance().run(task);
      PluginInstallOperation operation = pair.getFirst();
      if (operation == null) {
        return false;
      }

      Path oldFile = installedPlugin != null && !installedPlugin.isBundled() ?
                     installedPlugin.getPluginPath() :
                     null;

      boolean isRestartRequired = oldFile != null ||
                                  !DynamicPlugins.allowLoadUnloadWithoutRestart(pluginDescriptor) ||
                                  operation.isRestartRequired();
      if (isRestartRequired) {
        installAfterRestart(pluginDescriptor, path, oldFile, false);
      }
      ourState.onPluginInstall(pluginDescriptor, installedPlugin != null, isRestartRequired);

      IdeaPluginDescriptor toDisable = pair.getSecond();
      if (toDisable != null) {
        // TODO[yole] unload and check for restart
        pluginEnabler.disable(Set.of(toDisable));
      }

      Set<PluginInstallCallbackData> installedDependencies = operation.getInstalledDependentPlugins();
      List<IdeaPluginDescriptor> installedPlugins = new ArrayList<>();
      installedPlugins.add(pluginDescriptor);
      for (PluginInstallCallbackData plugin : installedDependencies) {
        installedPlugins.add(plugin.getPluginDescriptor());
      }

      Set<String> notInstalled = findNotInstalledPluginDependencies(pluginDescriptor.getDependencies(),
                                                                    model,
                                                                    ContainerUtil.map2Set(installedPlugins, PluginDescriptor::getPluginId));
      if (!notInstalled.isEmpty()) {
        String message = IdeBundle.message("dialog.message.plugin.depends.on.unknown.plugin",
                                           pluginDescriptor.getName(),
                                           notInstalled.size(),
                                           StringUtil.join(notInstalled, ", "));
        MessagesEx.showWarningDialog(parent, message, IdeBundle.message("dialog.title.install.plugin"));
      }

      PluginManagerMain.suggestToEnableInstalledDependantPlugins(pluginEnabler, installedPlugins);

      callback.accept(new PluginInstallCallbackData(path, pluginDescriptor, isRestartRequired));
      for (PluginInstallCallbackData callbackData : installedDependencies) {
        if (!callbackData.getPluginDescriptor().getPluginId().equals(pluginDescriptor.getPluginId())) {
          callback.accept(callbackData);
        }
      }

      if (path.toString().endsWith(".zip") && keepArchive()) {
        File tempFile = MarketplacePluginDownloadService.getPluginTempFile();
        FileUtil.copy(file, tempFile);
        MarketplacePluginDownloadService.renameFileToZipRoot(tempFile);
      }
      return true;
    }
    catch (IOException ex) {
      MessagesEx.showErrorDialog(parent, ex.getMessage(), CommonBundle.getErrorTitle());
      return false;
    }
  }
@RequiresEdt
  static boolean installFromDisk(@NotNull InstalledPluginsTableModel model,
                                 @NotNull PluginEnabler pluginEnabler,
                                 @NotNull File file,
                                 @Nullable Project project,
                                 @Nullable JComponent parent,
                                 @NotNull Consumer<? super PluginInstallCallbackData> callback)