blob: 9e75f7f6dcfc5a65bba476d9caa5ba0367a6b30a [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.externalSystem.util;
import com.intellij.execution.*;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.rmi.RemoteUtil;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.externalSystem.ExternalSystemManager;
import com.intellij.openapi.externalSystem.model.*;
import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.service.execution.AbstractExternalSystemTaskConfigurationType;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import com.intellij.openapi.externalSystem.service.internal.ExternalSystemResolveProjectTask;
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemIdeNotificationManager;
import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback;
import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
import com.intellij.openapi.externalSystem.service.project.manage.ModuleDataService;
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
import com.intellij.openapi.externalSystem.service.settings.ExternalSystemConfigLocator;
import com.intellij.openapi.externalSystem.service.task.ui.ExternalSystemRecentTasksList;
import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowEP;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
import com.intellij.openapi.wm.impl.ToolWindowImpl;
import com.intellij.ui.CheckBoxList;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.*;
import java.util.List;
/**
* @author Denis Zhdanov
* @since 4/22/13 9:36 AM
*/
public class ExternalSystemUtil {
private static final Logger LOG = Logger.getInstance("#" + ExternalSystemUtil.class.getName());
@NotNull private static final Map<String, String> RUNNER_IDS = ContainerUtilRt.newHashMap();
static {
RUNNER_IDS.put(DefaultRunExecutor.EXECUTOR_ID, ExternalSystemConstants.RUNNER_ID);
RUNNER_IDS.put(DefaultDebugExecutor.EXECUTOR_ID, ExternalSystemConstants.DEBUG_RUNNER_ID);
}
private ExternalSystemUtil() {
}
public static void ensureToolWindowInitialized(@NotNull Project project, @NotNull ProjectSystemId externalSystemId) {
ToolWindowManager manager = ToolWindowManager.getInstance(project);
if (!(manager instanceof ToolWindowManagerEx)) {
return;
}
ToolWindowManagerEx managerEx = (ToolWindowManagerEx)manager;
String id = externalSystemId.getReadableName();
ToolWindow window = manager.getToolWindow(id);
if (window != null) {
return;
}
ToolWindowEP[] beans = Extensions.getExtensions(ToolWindowEP.EP_NAME);
for (final ToolWindowEP bean : beans) {
if (id.equals(bean.id)) {
managerEx.initToolWindow(bean);
}
}
}
@SuppressWarnings("unchecked")
@Nullable
public static <T> T getToolWindowElement(@NotNull Class<T> clazz,
@NotNull Project project,
@NotNull DataKey<T> key,
@NotNull ProjectSystemId externalSystemId)
{
if (project.isDisposed() || !project.isOpen()) {
return null;
}
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
if (toolWindowManager == null) {
return null;
}
final ToolWindow toolWindow = toolWindowManager.getToolWindow(externalSystemId.getReadableName());
if (toolWindow == null) {
return null;
}
if (toolWindow instanceof ToolWindowImpl) {
((ToolWindowImpl)toolWindow).ensureContentInitialized();
}
final ContentManager contentManager = toolWindow.getContentManager();
if (contentManager == null) {
return null;
}
for (Content content : contentManager.getContents()) {
final JComponent component = content.getComponent();
if (component instanceof DataProvider) {
final Object data = ((DataProvider)component).getData(key.getName());
if (data != null && clazz.isInstance(data)) {
return (T)data;
}
}
}
return null;
}
/**
* Asks to refresh all external projects of the target external system linked to the given ide project.
* <p/>
* 'Refresh' here means 'obtain the most up-to-date version and apply it to the ide'.
*
* @param project target ide project
* @param externalSystemId target external system which projects should be refreshed
* @param force flag which defines if external project refresh should be performed if it's config is up-to-date
*/
public static void refreshProjects(@NotNull final Project project, @NotNull final ProjectSystemId externalSystemId, boolean force) {
refreshProjects(project, externalSystemId, force, false);
}
/**
* Asks to refresh all external projects of the target external system linked to the given ide project.
* <p/>
* 'Refresh' here means 'obtain the most up-to-date version and apply it to the ide'.
*
* @param project target ide project
* @param externalSystemId target external system which projects should be refreshed
* @param force flag which defines if external project refresh should be performed if it's config is up-to-date
*/
public static void refreshProjects(@NotNull final Project project, @NotNull final ProjectSystemId externalSystemId, boolean force, boolean modal) {
ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
if (manager == null) {
return;
}
AbstractExternalSystemSettings<?, ?, ?> settings = manager.getSettingsProvider().fun(project);
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 ExternalProjectRefreshCallback() {
@NotNull
private final Set<String> myExternalModulePaths = ContainerUtilRt.newHashSet();
@Override
public void onSuccess(@Nullable final DataNode<ProjectData> externalProject) {
if (externalProject == null) {
return;
}
Collection<DataNode<ModuleData>> moduleNodes = ExternalSystemApiUtil.findAll(externalProject, ProjectKeys.MODULE);
for (DataNode<ModuleData> node : moduleNodes) {
myExternalModulePaths.add(node.getData().getLinkedExternalProjectPath());
}
ExternalSystemApiUtil.executeProjectChangeAction(true, new Runnable() {
@Override
public void run() {
ProjectRootManagerEx.getInstanceEx(project).mergeRootsChangesDuring(new Runnable() {
@Override
public void run() {
projectDataManager.importData(externalProject.getKey(), Collections.singleton(externalProject), project, true);
}
});
}
});
if (--counter[0] <= 0) {
processOrphanModules();
}
}
@Override
public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) {
counter[0] = Integer.MAX_VALUE; // Don't process orphan modules if there was an error on refresh.
}
private void processOrphanModules() {
PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
List<Module> orphanIdeModules = ContainerUtilRt.newArrayList();
String externalSystemIdAsString = externalSystemId.toString();
for (Module module : platformFacade.getModules(project)) {
String s = module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY);
String p = module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY);
if (externalSystemIdAsString.equals(s) && !myExternalModulePaths.contains(p)) {
orphanIdeModules.add(module);
}
}
if (!orphanIdeModules.isEmpty()) {
ruleOrphanModules(orphanIdeModules, project, externalSystemId);
}
}
};
Map<String, Long> modificationStamps = manager.getLocalSettingsProvider().fun(project).getExternalConfigModificationStamps();
Set<String> toRefresh = ContainerUtilRt.newHashSet();
for (ExternalProjectSettings setting : projectsSettings) {
Long oldModificationStamp = modificationStamps.get(setting.getExternalProjectPath());
long currentModificationStamp = getTimeStamp(setting.getExternalProjectPath(), externalSystemId);
if (force || currentModificationStamp < 0 || oldModificationStamp == null || oldModificationStamp < currentModificationStamp) {
toRefresh.add(setting.getExternalProjectPath());
}
}
if (!toRefresh.isEmpty()) {
counter[0] = toRefresh.size();
for (String path : toRefresh) {
refreshProject(project, externalSystemId, path, callback, true, modal);
}
}
}
private static long getTimeStamp(@NotNull String path, @NotNull ProjectSystemId externalSystemId) {
VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(path));
if (vFile == null) {
return -1;
}
for (ExternalSystemConfigLocator locator : ExternalSystemConfigLocator.EP_NAME.getExtensions()) {
if (!externalSystemId.equals(locator.getTargetExternalSystemId())) {
continue;
}
VirtualFile adjusted = locator.adjust(vFile);
if (adjusted != null) {
vFile = adjusted;
}
}
return vFile.getTimeStamp();
}
/**
* There is a possible case that an external module has been un-linked from ide project. There are two ways to process
* ide modules which correspond to that external project:
* <pre>
* <ol>
* <li>Remove them from ide project as well;</li>
* <li>Keep them at ide project as well;</li>
* </ol>
* </pre>
* This method handles that situation, i.e. it asks a user what should be done and acts accordingly.
*
* @param orphanModules modules which correspond to the un-linked external project
* @param project current ide project
* @param externalSystemId id of the external system which project has been un-linked from ide project
*/
public static void ruleOrphanModules(@NotNull final List<Module> orphanModules,
@NotNull final Project project,
@NotNull final ProjectSystemId externalSystemId)
{
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
final JPanel content = new JPanel(new GridBagLayout());
content.add(new JLabel(ExternalSystemBundle.message("orphan.modules.text", externalSystemId.getReadableName())),
ExternalSystemUiUtil.getFillLineConstraints(0));
final CheckBoxList<Module> orphanModulesList = new CheckBoxList<Module>();
orphanModulesList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
orphanModulesList.setItems(orphanModules, new Function<Module, String>() {
@Override
public String fun(Module module) {
return module.getName();
}
});
for (Module module : orphanModules) {
orphanModulesList.setItemSelected(module, true);
}
orphanModulesList.setBorder(IdeBorderFactory.createEmptyBorder(8));
content.add(orphanModulesList, ExternalSystemUiUtil.getFillLineConstraints(0));
content.setBorder(IdeBorderFactory.createEmptyBorder(0, 0, 8, 0));
DialogWrapper dialog = new DialogWrapper(project) {
{
setTitle(ExternalSystemBundle.message("import.title", externalSystemId.getReadableName()));
init();
}
@Nullable
@Override
protected JComponent createCenterPanel() {
return new JBScrollPane(content);
}
};
boolean ok = dialog.showAndGet();
if (!ok) {
return;
}
List<Module> toRemove = ContainerUtilRt.newArrayList();
for (int i = 0; i < orphanModules.size(); i++) {
Module module = orphanModules.get(i);
if (orphanModulesList.isItemSelected(i)) {
toRemove.add(module);
}
else {
ModuleDataService.unlinkModuleFromExternalSystem(module);
}
}
if (!toRemove.isEmpty()) {
ServiceManager.getService(ProjectDataManager.class).removeData(ProjectKeys.MODULE, toRemove, project, true);
}
}
});
}
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
@Nullable
private static String extractDetails(@NotNull Throwable e) {
final Throwable unwrapped = RemoteUtil.unwrap(e);
if (unwrapped instanceof ExternalSystemException) {
return ((ExternalSystemException)unwrapped).getOriginalReason();
}
return null;
}
/**
* Queries slave gradle process to refresh target gradle project.
*
* @param project target intellij project to use
* @param externalProjectPath path of the target gradle project's file
* @param callback callback to be notified on refresh result
* @param resolveLibraries flag that identifies whether gradle libraries should be resolved during the refresh
* @return the most up-to-date gradle project (if any)
*/
public static void refreshProject(@NotNull final Project project,
@NotNull final ProjectSystemId externalSystemId,
@NotNull final String externalProjectPath,
@NotNull final ExternalProjectRefreshCallback callback,
final boolean resolveLibraries,
final boolean modal) {
refreshProject(project, externalSystemId, externalProjectPath, callback, resolveLibraries, modal, true);
}
/**
* Queries slave gradle process to refresh target gradle project.
*
* @param project target intellij project to use
* @param externalProjectPath path of the target gradle project's file
* @param callback callback to be notified on refresh result
* @param resolveLibraries flag that identifies whether gradle libraries should be resolved during the refresh
* @param reportRefreshError prevent to show annoying error notification, e.g. if auto-import mode used
* @return the most up-to-date gradle project (if any)
*/
public static void refreshProject(@NotNull final Project project,
@NotNull final ProjectSystemId externalSystemId,
@NotNull final String externalProjectPath,
@NotNull final ExternalProjectRefreshCallback callback,
final boolean resolveLibraries,
final boolean modal,
final boolean reportRefreshError)
{
File projectFile = new File(externalProjectPath);
final String projectName;
if (projectFile.isFile()) {
projectName = projectFile.getParentFile().getName();
}
else {
projectName = projectFile.getName();
}
final TaskUnderProgress refreshProjectStructureTask = new TaskUnderProgress() {
@SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "IOResourceOpenedButNotSafelyClosed"})
@Override
public void execute(@NotNull ProgressIndicator indicator) {
ExternalSystemResolveProjectTask task
= new ExternalSystemResolveProjectTask(externalSystemId, project, externalProjectPath, resolveLibraries);
task.execute(indicator);
final Throwable error = task.getError();
if (error == null) {
long stamp = getTimeStamp(externalProjectPath, externalSystemId);
if (stamp > 0) {
ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
assert manager != null;
manager.getLocalSettingsProvider().fun(project).getExternalConfigModificationStamps().put(externalProjectPath, stamp);
}
DataNode<ProjectData> externalProject = task.getExternalProject();
callback.onSuccess(externalProject);
return;
}
String message = ExternalSystemApiUtil.buildErrorMessage(error);
if (StringUtil.isEmpty(message)) {
message = String.format(
"Can't resolve %s project at '%s'. Reason: %s",
externalSystemId.getReadableName(), externalProjectPath, message
);
}
callback.onFailure(message, extractDetails(error));
ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
if(manager == null) {
return;
}
AbstractExternalSystemSettings<?, ?, ?> settings = manager.getSettingsProvider().fun(project);
ExternalProjectSettings projectSettings = settings.getLinkedProjectSettings(externalProjectPath);
if (projectSettings == null || !reportRefreshError) {
return;
}
ExternalSystemIdeNotificationManager notificationManager = ServiceManager.getService(ExternalSystemIdeNotificationManager.class);
if (notificationManager != null) {
notificationManager.processExternalProjectRefreshError(error, project, projectName, externalSystemId);
}
}
};
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
if (modal) {
String title = ExternalSystemBundle.message("progress.import.text", projectName, externalSystemId.getReadableName());
new Task.Backgroundable(project, title, true, PerformInBackgroundOption.DEAF) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
refreshProjectStructureTask.execute(indicator);
}
}.queue();
}
else {
String title = ExternalSystemBundle.message("progress.refresh.text", projectName, externalSystemId.getReadableName());
new Task.Backgroundable(project, title) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
refreshProjectStructureTask.execute(indicator);
}
}.queue();
}
}
});
}
public static void runTask(@NotNull ExternalSystemTaskExecutionSettings taskSettings,
@NotNull String executorId,
@NotNull Project project,
@NotNull ProjectSystemId externalSystemId)
{
Executor executor = ExecutorRegistry.getInstance().getExecutorById(executorId);
if (executor == null) {
return;
}
String runnerId = getRunnerId(executorId);
if (runnerId == null) {
return;
}
ProgramRunner runner = RunnerRegistry.getInstance().findRunnerById(runnerId);
if (runner == null) {
return;
}
AbstractExternalSystemTaskConfigurationType configurationType = findConfigurationType(externalSystemId);
if (configurationType == null) {
return;
}
String name = AbstractExternalSystemTaskConfigurationType.generateName(project, taskSettings);
RunnerAndConfigurationSettings settings = RunManager.getInstance(project).createRunConfiguration(name, configurationType.getFactory());
ExternalSystemRunConfiguration runConfiguration = (ExternalSystemRunConfiguration)settings.getConfiguration();
runConfiguration.getSettings().setExternalProjectPath(taskSettings.getExternalProjectPath());
runConfiguration.getSettings().setTaskNames(taskSettings.getTaskNames());
ExecutionEnvironment env = new ExecutionEnvironment(executor, runner, settings, project);
try {
runner.execute(env, null);
}
catch (ExecutionException e) {
LOG.warn("Can't execute task " + taskSettings, e);
}
}
@Nullable
public static AbstractExternalSystemTaskConfigurationType findConfigurationType(@NotNull ProjectSystemId externalSystemId) {
for (ConfigurationType type : Extensions.getExtensions(ConfigurationType.CONFIGURATION_TYPE_EP)) {
if (type instanceof AbstractExternalSystemTaskConfigurationType) {
AbstractExternalSystemTaskConfigurationType candidate = (AbstractExternalSystemTaskConfigurationType)type;
if (externalSystemId.equals(candidate.getExternalSystemId())) {
return candidate;
}
}
}
return null;
}
/**
* 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());
}
@Nullable
public static String getRunnerId(@NotNull String executorId) {
return RUNNER_IDS.get(executorId);
}
/**
* Allows to answer if given ide project has 1-1 mapping with the given external project, i.e. the ide project has been
* imported from external system and no other external projects have been added.
* <p/>
* This might be necessary in a situation when project-level setting is changed (e.g. project name). We don't want to rename
* ide project if it doesn't completely corresponds to the given ide project then.
*
* @param ideProject target ide project
* @param externalProject target external project
* @return <code>true</code> if given ide project has 1-1 mapping to the given external project;
* <code>false</code> otherwise
*/
public static boolean isOneToOneMapping(@NotNull Project ideProject, @NotNull DataNode<ProjectData> externalProject) {
String linkedExternalProjectPath = null;
for (ExternalSystemManager<?, ?, ?, ?, ?> manager : ExternalSystemApiUtil.getAllManagers()) {
ProjectSystemId externalSystemId = manager.getSystemId();
AbstractExternalSystemSettings systemSettings = ExternalSystemApiUtil.getSettings(ideProject, externalSystemId);
Collection projectsSettings = systemSettings.getLinkedProjectsSettings();
int linkedProjectsNumber = projectsSettings.size();
if (linkedProjectsNumber > 1) {
// More than one external project of the same external system type is linked to the given ide project.
return false;
}
else if (linkedProjectsNumber == 1) {
if (linkedExternalProjectPath == null) {
// More than one external project of different external system types is linked to the current ide project.
linkedExternalProjectPath = ((ExternalProjectSettings)projectsSettings.iterator().next()).getExternalProjectPath();
}
else {
return false;
}
}
}
ProjectData projectData = externalProject.getData();
if (linkedExternalProjectPath != null && !linkedExternalProjectPath.equals(projectData.getLinkedExternalProjectPath())) {
// New external project is being linked.
return false;
}
Set<String> externalModulePaths = ContainerUtilRt.newHashSet();
for (DataNode<ModuleData> moduleNode : ExternalSystemApiUtil.findAll(externalProject, ProjectKeys.MODULE)) {
externalModulePaths.add(moduleNode.getData().getLinkedExternalProjectPath());
}
externalModulePaths.remove(linkedExternalProjectPath);
PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
for (Module module : platformFacade.getModules(ideProject)) {
String path = module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY);
if (!StringUtil.isEmpty(path) && !externalModulePaths.remove(path)) {
return false;
}
}
return externalModulePaths.isEmpty();
}
/**
* Tries to obtain external project info implied by the given settings and link that external project to the given ide project.
*
* @param externalSystemId target external system
* @param projectSettings settings of the external project to link
* @param project target ide project to link external project to
* @param executionResultCallback it might take a while to resolve external project info, that's why it's possible to provide
* a callback to be notified on processing result. It receives <code>true</code> if an external
* project has been successfully linked to the given ide project;
* <code>false</code> otherwise (note that corresponding notification with error details is expected
* to be shown to the end-user then)
* @param resolveLibraries flag which identifies if missing external project binaries should be downloaded
* @param modal flag which identifies if progress bar which represents current processing state should be modal
*/
@SuppressWarnings("UnusedDeclaration")
public static void linkExternalProject(@NotNull final ProjectSystemId externalSystemId,
@NotNull final ExternalProjectSettings projectSettings,
@NotNull final Project project,
@Nullable final Consumer<Boolean> executionResultCallback,
boolean resolveLibraries,
boolean modal)
{
ExternalProjectRefreshCallback callback = new ExternalProjectRefreshCallback() {
@SuppressWarnings("unchecked")
@Override
public void onSuccess(@Nullable final DataNode<ProjectData> externalProject) {
if (externalProject == null) {
if (executionResultCallback != null) {
executionResultCallback.consume(false);
}
return;
}
AbstractExternalSystemSettings systemSettings = ExternalSystemApiUtil.getSettings(project, externalSystemId);
Set<ExternalProjectSettings> projects = ContainerUtilRt.newHashSet(systemSettings.getLinkedProjectsSettings());
projects.add(projectSettings);
systemSettings.setLinkedProjectsSettings(projects);
ensureToolWindowInitialized(project, externalSystemId);
ExternalSystemApiUtil.executeProjectChangeAction(new Runnable() {
@Override
public void run() {
ProjectRootManagerEx.getInstanceEx(project).mergeRootsChangesDuring(new Runnable() {
@Override
public void run() {
ProjectDataManager dataManager = ServiceManager.getService(ProjectDataManager.class);
dataManager.importData(externalProject.getKey(), Collections.singleton(externalProject), project, true);
}
});
}
});
if (executionResultCallback != null) {
executionResultCallback.consume(true);
}
}
@Override
public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) {
if (executionResultCallback != null) {
executionResultCallback.consume(false);
}
}
};
refreshProject(project, externalSystemId, projectSettings.getExternalProjectPath(), callback, resolveLibraries, modal);
}
private interface TaskUnderProgress {
void execute(@NotNull ProgressIndicator indicator);
}
}