@Override
  protected void processData(
      @NotNull final Collection<DataNode<ModuleData>> nodes, @NotNull Project project) {
    if (nodes.isEmpty()) {
      return;
    }
    ProjectSystemId externalSystemId = nodes.iterator().next().getData().getOwner();
    ExternalSystemManager<?, ?, ?, ?, ?> manager =
        ExternalSystemApiUtil.getManager(externalSystemId);
    assert manager != null;

    final MultiMap<DataNode<ProjectData>, DataNode<ModuleData>> grouped =
        ExternalSystemApiUtil.groupBy(nodes, ProjectKeys.PROJECT);
    Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> data = ContainerUtilRt.newHashMap();
    for (Map.Entry<DataNode<ProjectData>, Collection<DataNode<ModuleData>>> entry :
        grouped.entrySet()) {
      data.put(
          ExternalProjectPojo.from(entry.getKey().getData()),
          ContainerUtilRt.map2List(entry.getValue(), MAPPER));
    }

    AbstractExternalSystemLocalSettings settings = manager.getLocalSettingsProvider().fun(project);
    Set<String> pathsToForget = detectRenamedProjects(data, settings.getAvailableProjects());
    if (!pathsToForget.isEmpty()) {
      settings.forgetExternalProjects(pathsToForget);
    }
    Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> projects =
        ContainerUtilRt.newHashMap(settings.getAvailableProjects());
    projects.putAll(data);
    settings.setAvailableProjects(projects);
  }
  @Override
  protected void processData(
      @NotNull Collection<DataNode<TaskData>> nodes, @NotNull Project project) {
    if (nodes.isEmpty()) {
      return;
    }
    ProjectSystemId externalSystemId = nodes.iterator().next().getData().getOwner();
    ExternalSystemManager<?, ?, ?, ?, ?> manager =
        ExternalSystemApiUtil.getManager(externalSystemId);
    assert manager != null;

    ExternalSystemKeymapExtension.updateActions(project, nodes);

    MultiMap<ExternalConfigPathAware, DataNode<TaskData>> grouped =
        ContainerUtil.groupBy(nodes, TASK_HOLDER_RETRIEVAL_STRATEGY);
    Map<String, Collection<ExternalTaskPojo>> data = ContainerUtilRt.newHashMap();
    for (Map.Entry<ExternalConfigPathAware, Collection<DataNode<TaskData>>> entry :
        grouped.entrySet()) {
      data.put(
          entry.getKey().getLinkedExternalProjectPath(),
          ContainerUtilRt.map2List(entry.getValue(), MAPPER));
    }

    AbstractExternalSystemLocalSettings settings = manager.getLocalSettingsProvider().fun(project);
    Map<String, Collection<ExternalTaskPojo>> availableTasks =
        ContainerUtilRt.newHashMap(settings.getAvailableTasks());
    availableTasks.putAll(data);
    settings.setAvailableTasks(availableTasks);
  }
 @SuppressWarnings("unchecked")
 public static <S extends AbstractExternalSystemLocalSettings> S getLocalSettings(
     @NotNull Project project, @NotNull ProjectSystemId externalSystemId)
     throws IllegalArgumentException {
   ExternalSystemManager<?, ?, ?, ?, ?> manager = getManager(externalSystemId);
   if (manager == null) {
     throw new IllegalArgumentException(
         String.format(
             "Can't retrieve local external system settings for id '%s'. Reason: no such external system is registered",
             externalSystemId.getReadableName()));
   }
   return (S) manager.getLocalSettingsProvider().fun(project);
 }
  /**
   * Is expected to be called when given task info is about to be executed.
   *
   * <p>Basically, this method updates recent tasks list at the corresponding external system tool
   * window and persists new recent tasks state.
   *
   * @param taskInfo task which is about to be executed
   * @param project target project
   */
  public static void updateRecentTasks(
      @NotNull ExternalTaskExecutionInfo taskInfo, @NotNull Project project) {
    ProjectSystemId externalSystemId = taskInfo.getSettings().getExternalSystemId();
    ExternalSystemRecentTasksList recentTasksList =
        getToolWindowElement(
            ExternalSystemRecentTasksList.class,
            project,
            ExternalSystemDataKeys.RECENT_TASKS_LIST,
            externalSystemId);
    if (recentTasksList == null) {
      return;
    }
    recentTasksList.setFirst(taskInfo);

    ExternalSystemManager<?, ?, ?, ?, ?> manager =
        ExternalSystemApiUtil.getManager(externalSystemId);
    assert manager != null;
    AbstractExternalSystemLocalSettings settings = manager.getLocalSettingsProvider().fun(project);
    settings.setRecentTasks(recentTasksList.getModel().getTasks());
  }
  @Override
  public void importData(
      @NotNull final Collection<DataNode<BuildScriptClasspathData>> toImport,
      @NotNull final Project project,
      boolean synchronous) {
    if (toImport.isEmpty()) {
      return;
    }
    if (!project.isInitialized()) {
      return;
    }

    final GradleInstallationManager gradleInstallationManager =
        ServiceManager.getService(GradleInstallationManager.class);

    ExternalSystemManager<?, ?, ?, ?, ?> manager =
        ExternalSystemApiUtil.getManager(GradleConstants.SYSTEM_ID);
    assert manager != null;
    AbstractExternalSystemLocalSettings localSettings =
        manager.getLocalSettingsProvider().fun(project);

    //noinspection MismatchedQueryAndUpdateOfCollection
    Map<String /* externalProjectPath */, Set<String>> externalProjectGradleSdkLibs =
        new FactoryMap<String, Set<String>>() {
          @Nullable
          @Override
          protected Set<String> create(String externalProjectPath) {
            GradleProjectSettings settings =
                GradleSettings.getInstance(project).getLinkedProjectSettings(externalProjectPath);
            if (settings == null || settings.getDistributionType() == null) return null;

            final Set<String> gradleSdkLibraries = ContainerUtil.newLinkedHashSet();
            File gradleHome =
                gradleInstallationManager.getGradleHome(
                    settings.getDistributionType(), externalProjectPath, settings.getGradleHome());
            if (gradleHome != null && gradleHome.isDirectory()) {

              final Collection<File> libraries =
                  gradleInstallationManager.getClassRoots(project, externalProjectPath);
              if (libraries != null) {
                for (File library : libraries) {
                  gradleSdkLibraries.add(FileUtil.toCanonicalPath(library.getPath()));
                }
              }
            }
            return gradleSdkLibraries;
          }
        };

    for (final DataNode<BuildScriptClasspathData> node : toImport) {
      if (GradleConstants.SYSTEM_ID.equals(node.getData().getOwner())) {

        DataNode<ProjectData> projectDataNode =
            ExternalSystemApiUtil.findParent(node, ProjectKeys.PROJECT);
        assert projectDataNode != null;

        String linkedExternalProjectPath = projectDataNode.getData().getLinkedExternalProjectPath();
        DataNode<ModuleData> moduleDataNode =
            ExternalSystemApiUtil.findParent(node, ProjectKeys.MODULE);
        if (moduleDataNode == null) continue;

        String externalModulePath = moduleDataNode.getData().getLinkedExternalProjectPath();
        GradleProjectSettings settings =
            GradleSettings.getInstance(project).getLinkedProjectSettings(linkedExternalProjectPath);
        if (settings == null || settings.getDistributionType() == null) continue;

        final Set<String> buildClasspath = ContainerUtil.newLinkedHashSet();
        BuildScriptClasspathData buildScriptClasspathData = node.getData();
        for (BuildScriptClasspathData.ClasspathEntry classpathEntry :
            buildScriptClasspathData.getClasspathEntries()) {
          for (String path : classpathEntry.getSourcesFile()) {
            buildClasspath.add(FileUtil.toCanonicalPath(path));
          }

          for (String path : classpathEntry.getClassesFile()) {
            buildClasspath.add(FileUtil.toCanonicalPath(path));
          }
        }

        ExternalProjectBuildClasspathPojo projectBuildClasspathPojo =
            localSettings.getProjectBuildClasspath().get(linkedExternalProjectPath);
        if (projectBuildClasspathPojo == null) {
          projectBuildClasspathPojo =
              new ExternalProjectBuildClasspathPojo(
                  moduleDataNode.getData().getExternalName(),
                  ContainerUtil.<String>newArrayList(),
                  ContainerUtil.<String, ExternalModuleBuildClasspathPojo>newHashMap());
          localSettings
              .getProjectBuildClasspath()
              .put(linkedExternalProjectPath, projectBuildClasspathPojo);
        }

        List<String> projectBuildClasspath =
            ContainerUtil.newArrayList(externalProjectGradleSdkLibs.get(linkedExternalProjectPath));
        // add main java root of buildSrc project
        projectBuildClasspath.add(linkedExternalProjectPath + "/buildSrc/src/main/java");
        // add main groovy root of buildSrc project
        projectBuildClasspath.add(linkedExternalProjectPath + "/buildSrc/src/main/groovy");

        projectBuildClasspathPojo.setProjectBuildClasspath(projectBuildClasspath);
        projectBuildClasspathPojo
            .getModulesBuildClasspath()
            .put(
                externalModulePath,
                new ExternalModuleBuildClasspathPojo(
                    externalModulePath, ContainerUtil.newArrayList(buildClasspath)));
      }
    }

    GradleBuildClasspathManager.getInstance(project).reload();
  }
  public ExternalSystemTasksPanel(
      @NotNull Project project,
      @NotNull ProjectSystemId externalSystemId,
      @NotNull NotificationGroup notificationGroup) {
    super(true);
    myExternalSystemId = externalSystemId;
    myNotificationGroup = notificationGroup;
    myProject = project;

    ExternalSystemManager<?, ?, ?, ?, ?> manager =
        ExternalSystemApiUtil.getManager(externalSystemId);
    assert manager != null;
    AbstractExternalSystemLocalSettings settings = manager.getLocalSettingsProvider().fun(project);

    ExternalSystemRecentTaskListModel recentTasksModel =
        new ExternalSystemRecentTaskListModel(externalSystemId, project);
    recentTasksModel.setTasks(settings.getRecentTasks());
    myRecentTasksList =
        new ExternalSystemRecentTasksList(recentTasksModel, externalSystemId, project) {
          @Override
          protected void processMouseEvent(MouseEvent e) {
            if (e.getClickCount() > 0) {
              mySelectedTaskProvider = myRecentTasksList;
              myAllTasksTree.getSelectionModel().clearSelection();
            }
            super.processMouseEvent(e);
          }
        };

    myAllTasksModel = new ExternalSystemTasksTreeModel(externalSystemId);
    myAllTasksTree =
        new ExternalSystemTasksTree(
            myAllTasksModel, settings.getExpandStates(), project, externalSystemId) {
          @Override
          protected void processMouseEvent(MouseEvent e) {
            if (e.getClickCount() > 0) {
              mySelectedTaskProvider = myAllTasksTree;
              myRecentTasksList.getSelectionModel().clearSelection();
            }
            super.processMouseEvent(e);
          }
        };
    final String actionIdToUseForDoubleClick =
        DefaultRunExecutor.getRunExecutorInstance().getContextActionId();
    myAllTasksTree.addMouseListener(
        new MouseAdapter() {
          @Override
          public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() >= 2 && !e.isPopupTrigger()) {
              ExternalSystemUiUtil.executeAction(actionIdToUseForDoubleClick, e);
            }
          }
        });
    ExternalSystemUiUtil.apply(settings, myAllTasksModel);
    CustomizationUtil.installPopupHandler(
        myAllTasksTree, TREE_ACTIONS_GROUP_ID, TREE_CONTEXT_MENU_PLACE);

    ActionManager actionManager = ActionManager.getInstance();
    ActionGroup group = (ActionGroup) actionManager.getAction(TOOL_WINDOW_TOOLBAR_ACTIONS_GROUP_ID);
    ActionToolbar toolbar = actionManager.createActionToolbar(TOOL_WINDOW_PLACE, group, true);
    toolbar.setTargetComponent(this);
    setToolbar(toolbar.getComponent());

    JPanel content = new JPanel(new GridBagLayout());
    content.setOpaque(true);
    content.setBackground(UIUtil.getListBackground());
    JComponent recentTasksWithTitle =
        wrap(myRecentTasksList, ExternalSystemBundle.message("tasks.recent.title"));
    content.add(recentTasksWithTitle, ExternalSystemUiUtil.getFillLineConstraints(0));
    JBScrollPane scrollPane = new JBScrollPane(myAllTasksTree);
    scrollPane.setBorder(null);
    JComponent allTasksWithTitle =
        wrap(scrollPane, ExternalSystemBundle.message("tasks.all.title"));
    content.add(
        allTasksWithTitle, ExternalSystemUiUtil.getFillLineConstraints(0).weighty(1).fillCell());
    setContent(content);
  }
  /**
   * Asks to refresh all external projects of the target external system linked to the given ide
   * project based on provided spec
   *
   * @param specBuilder import specification builder
   */
  public static void refreshProjects(@NotNull final ImportSpecBuilder specBuilder) {
    ImportSpec spec = specBuilder.build();

    ExternalSystemManager<?, ?, ?, ?, ?> manager =
        ExternalSystemApiUtil.getManager(spec.getExternalSystemId());
    if (manager == null) {
      return;
    }
    AbstractExternalSystemSettings<?, ?, ?> settings =
        manager.getSettingsProvider().fun(spec.getProject());
    final Collection<? extends ExternalProjectSettings> projectsSettings =
        settings.getLinkedProjectsSettings();
    if (projectsSettings.isEmpty()) {
      return;
    }

    final ProjectDataManager projectDataManager =
        ServiceManager.getService(ProjectDataManager.class);
    final int[] counter = new int[1];

    ExternalProjectRefreshCallback callback =
        new MyMultiExternalProjectRefreshCallback(
            spec.getProject(), projectDataManager, counter, spec.getExternalSystemId());

    Map<String, Long> modificationStamps =
        manager
            .getLocalSettingsProvider()
            .fun(spec.getProject())
            .getExternalConfigModificationStamps();
    Set<String> toRefresh = ContainerUtilRt.newHashSet();
    for (ExternalProjectSettings setting : projectsSettings) {

      // don't refresh project when auto-import is disabled if such behavior needed (e.g. on project
      // opening when auto-import is disabled)
      if (!setting.isUseAutoImport() && spec.isWhenAutoImportEnabled()) continue;

      if (spec.isForceWhenUptodate()) {
        toRefresh.add(setting.getExternalProjectPath());
      } else {
        Long oldModificationStamp = modificationStamps.get(setting.getExternalProjectPath());
        long currentModificationStamp = getTimeStamp(setting, spec.getExternalSystemId());
        if (oldModificationStamp == null || oldModificationStamp < currentModificationStamp) {
          toRefresh.add(setting.getExternalProjectPath());
        }
      }
    }

    if (!toRefresh.isEmpty()) {
      ExternalSystemNotificationManager.getInstance(spec.getProject())
          .clearNotifications(null, NotificationSource.PROJECT_SYNC, spec.getExternalSystemId());

      counter[0] = toRefresh.size();
      for (String path : toRefresh) {
        refreshProject(
            spec.getProject(),
            spec.getExternalSystemId(),
            path,
            callback,
            false,
            spec.getProgressExecutionMode());
      }
    }
  }