blob: fae9ee0d88c16fb1ca1baf738a5933f231803386 [file] [log] [blame]
/*
* Copyright 2000-2014 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 org.jetbrains.plugins.gradle.service.project.data;
import com.intellij.CommonBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.*;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
import com.intellij.openapi.externalSystem.service.internal.ExternalSystemResolveProjectTask;
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemNotificationManager;
import com.intellij.openapi.externalSystem.service.notification.NotificationSource;
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataService;
import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
import com.intellij.openapi.externalSystem.util.*;
import com.intellij.openapi.module.Module;
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.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ConcurrentFactoryMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Collection;
import java.util.Map;
/**
* @author Vladislav.Soroka
* @since 7/17/2014
*/
@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
public class ExternalProjectDataService implements ProjectDataService<ExternalProject, Project> {
private static final Logger LOG = Logger.getInstance(ExternalProjectDataService.class);
@NotNull public static final Key<ExternalProject> KEY = Key.create(ExternalProject.class, ProjectKeys.TASK.getProcessingWeight() + 1);
@NotNull private final Map<Pair<ProjectSystemId, File>, ExternalProject> myExternalRootProjects;
@NotNull private ProjectDataManager myProjectDataManager;
public ExternalProjectDataService(@NotNull ProjectDataManager projectDataManager) {
myProjectDataManager = projectDataManager;
myExternalRootProjects = new ConcurrentFactoryMap<Pair<ProjectSystemId, File>, ExternalProject>() {
@Nullable
@Override
protected ExternalProject create(Pair<ProjectSystemId, File> key) {
return new ExternalProjectSerializer().load(key.first, key.second);
}
@Override
public ExternalProject put(Pair<ProjectSystemId, File> key, ExternalProject value) {
new ExternalProjectSerializer().save(value);
return super.put(key, value);
}
};
}
@NotNull
@Override
public Key<ExternalProject> getTargetDataKey() {
return KEY;
}
public void importData(@NotNull final Collection<DataNode<ExternalProject>> toImport,
@NotNull final Project project,
final boolean synchronous) {
if (toImport.size() != 1) {
throw new IllegalArgumentException(
String.format("Expected to get a single external project but got %d: %s", toImport.size(), toImport));
}
saveExternalProject(toImport.iterator().next().getData());
}
@Override
public void removeData(@NotNull final Collection<? extends Project> modules, @NotNull Project project, boolean synchronous) {
}
@Nullable
public ExternalProject getOrImportRootExternalProject(@NotNull Project project,
@NotNull ProjectSystemId systemId,
@NotNull File projectRootDir) {
final ExternalProject externalProject = getRootExternalProject(systemId, projectRootDir);
return externalProject != null ? externalProject : importExternalProject(project, systemId, projectRootDir);
}
@Nullable
private ExternalProject importExternalProject(@NotNull final Project project,
@NotNull final ProjectSystemId projectSystemId,
@NotNull final File projectRootDir) {
final Boolean result = UIUtil.invokeAndWaitIfNeeded(new Computable<Boolean>() {
@Override
public Boolean compute() {
final Ref<Boolean> result = new Ref<Boolean>(false);
if (project.isDisposed()) return false;
final String linkedProjectPath = FileUtil.toCanonicalPath(projectRootDir.getPath());
final ExternalProjectSettings projectSettings =
ExternalSystemApiUtil.getSettings(project, projectSystemId).getLinkedProjectSettings(linkedProjectPath);
if (projectSettings == null) {
LOG.warn("Unable to get project settings for project path: " + linkedProjectPath);
if (LOG.isDebugEnabled()) {
LOG.debug("Available projects paths: " + ContainerUtil.map(
ExternalSystemApiUtil.getSettings(project, projectSystemId).getLinkedProjectsSettings(),
new Function<ExternalProjectSettings, String>() {
@Override
public String fun(ExternalProjectSettings settings) {
return settings.getExternalProjectPath();
}
}));
}
return false;
}
final File projectFile = new File(linkedProjectPath);
final String projectName;
if (projectFile.isFile()) {
projectName = projectFile.getParentFile().getName();
}
else {
projectName = projectFile.getName();
}
// ask a user for the project import if auto-import is disabled
if (!projectSettings.isUseAutoImport()) {
String message = String.format("Project '%s' require synchronization with %s configuration. \nImport the project?",
projectName, projectSystemId.getReadableName());
int returnValue = Messages.showOkCancelDialog(
message, "Import Project", CommonBundle.getOkButtonText(), CommonBundle.getCancelButtonText(), Messages.getQuestionIcon()
);
if (returnValue != Messages.OK) return false;
}
final String title = ExternalSystemBundle.message("progress.import.text", linkedProjectPath, projectSystemId.getReadableName());
new Task.Modal(project, title, false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
if (project.isDisposed()) return;
ExternalSystemNotificationManager.getInstance(project)
.clearNotifications(null, NotificationSource.PROJECT_SYNC, projectSystemId);
ExternalSystemResolveProjectTask task =
new ExternalSystemResolveProjectTask(projectSystemId, project, linkedProjectPath, false);
task.execute(indicator, ExternalSystemTaskNotificationListener.EP_NAME.getExtensions());
if (project.isDisposed()) return;
final Throwable error = task.getError();
if (error != null) {
ExternalSystemNotificationManager.getInstance(project)
.processExternalProjectRefreshError(error, projectName, projectSystemId);
return;
}
final DataNode<ProjectData> projectDataDataNode = task.getExternalProject();
if (projectDataDataNode == null) return;
final Collection<DataNode<ExternalProject>> nodes = ExternalSystemApiUtil.findAll(projectDataDataNode, KEY);
if (nodes.size() != 1) {
throw new IllegalArgumentException(
String.format("Expected to get a single external project but got %d: %s", nodes.size(), nodes));
}
ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(new Runnable() {
@Override
public void run() {
myProjectDataManager.importData(KEY, nodes, project, true);
}
});
result.set(true);
}
}.queue();
return result.get();
}
});
return result ? getRootExternalProject(projectSystemId, projectRootDir) : null;
}
@Nullable
public ExternalProject getRootExternalProject(@NotNull ProjectSystemId systemId, @NotNull File projectRootDir) {
return myExternalRootProjects.get(Pair.create(systemId, projectRootDir));
}
public void saveExternalProject(@NotNull ExternalProject externalProject) {
myExternalRootProjects.put(
Pair.create(new ProjectSystemId(externalProject.getExternalSystemId()), externalProject.getProjectDir()),
new DefaultExternalProject(externalProject)
);
}
@Nullable
public ExternalProject findExternalProject(@NotNull ExternalProject parentProject, @NotNull Module module) {
String externalProjectId = ExternalSystemApiUtil.getExternalProjectId(module);
return externalProjectId != null ? findExternalProject(parentProject, externalProjectId) : null;
}
@Nullable
private static ExternalProject findExternalProject(@NotNull ExternalProject parentProject, @NotNull String externalProjectId) {
if (parentProject.getQName().equals(externalProjectId)) return parentProject;
if (parentProject.getChildProjects().containsKey(externalProjectId)) {
return parentProject.getChildProjects().get(externalProjectId);
}
for (ExternalProject externalProject : parentProject.getChildProjects().values()) {
final ExternalProject project = findExternalProject(externalProject, externalProjectId);
if (project != null) return project;
}
return null;
}
}