refactored external system services to reduce amount of write actions

This commit is contained in:
Fedor Korotkov
2015-09-08 14:57:50 +03:00
committed by nik
parent 8ce42c3ea9
commit ad5f32d37c
11 changed files with 554 additions and 496 deletions
@@ -1,6 +1,5 @@
package com.intellij.openapi.externalSystem.service.project;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.project.*;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
@@ -60,8 +59,7 @@ public interface PlatformFacade {
/**
* Creates a module of the specified type at the specified path and adds it to the project
* to which the module manager is related. {@link #commit()} must be called to
* bring the changes in effect.
* to which the module manager is related.
*
*
* @param project
@@ -26,6 +26,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.ExternalSystemAutoImportAware;
import com.intellij.openapi.externalSystem.ExternalSystemManager;
import com.intellij.openapi.externalSystem.model.*;
import com.intellij.openapi.externalSystem.model.Key;
import com.intellij.openapi.externalSystem.model.project.LibraryData;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
@@ -39,12 +40,10 @@ import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListen
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
@@ -446,6 +445,61 @@ public class ExternalSystemApiUtil {
return null;
}
public static void commitChangedModels(boolean synchronous, Project project, List<Library.ModifiableModel> models) {
final List<Library.ModifiableModel> changedModels = ContainerUtil.findAll(models, new Condition<Library.ModifiableModel>() {
@Override
public boolean value(Library.ModifiableModel model) {
return model.isChanged();
}
});
if (!changedModels.isEmpty()) {
executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
for (Library.ModifiableModel modifiableModel : changedModels) {
modifiableModel.commit();
}
}
});
}
}
public static void disposeModels(@NotNull Collection<ModifiableRootModel> models) {
for (ModifiableRootModel model : models) {
if (!model.isDisposed()) {
model.dispose();
}
}
}
public static void commitModels(boolean synchronous, Project project, List<ModifiableRootModel> models) {
final List<ModifiableRootModel> changedModels = ContainerUtilRt.newArrayList();
for (ModifiableRootModel modifiableRootModel : models) {
if (modifiableRootModel.isDisposed()) {
continue;
}
if (modifiableRootModel.isChanged()) {
changedModels.add(modifiableRootModel);
} else {
modifiableRootModel.dispose();
}
}
// Commit only if there are changes. #executeProjectChangeAction acquires a write lock
if (!changedModels.isEmpty()) {
executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
for (ModifiableRootModel modifiableRootModel : changedModels) {
// double check
if (!modifiableRootModel.isDisposed()) {
modifiableRootModel.commit();
}
}
}
});
}
}
public static void executeProjectChangeAction(@NotNull final DisposeAwareProjectChange task) {
executeProjectChangeAction(false, task);
}
@@ -28,6 +28,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Consumer;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.containers.MultiMap;
@@ -133,15 +134,27 @@ public abstract class AbstractDependencyDataService<E extends AbstractDependency
}
@Override
public void removeData(@NotNull final Computable<Collection<I>> toRemoveComputable,
@NotNull final Collection<DataNode<E>> toIgnore,
@NotNull final ProjectData projectData,
@NotNull final Project project,
@NotNull final PlatformFacade platformFacade,
final boolean synchronous) {
Map<Module, Collection<ExportableOrderEntry>> byModule = groupByModule(toRemoveComputable.compute());
for (Map.Entry<Module, Collection<ExportableOrderEntry>> entry : byModule.entrySet()) {
removeData(entry.getValue(), entry.getKey(), platformFacade, synchronous);
public void removeData(@NotNull Computable<Collection<I>> toRemoveComputable,
@NotNull Collection<DataNode<E>> toIgnore,
@NotNull ProjectData projectData,
@NotNull Project project,
@NotNull PlatformFacade platformFacade,
boolean synchronous) {
List<ModifiableRootModel> models = ContainerUtilRt.newArrayList();
try {
Map<Module, Collection<ExportableOrderEntry>> byModule = groupByModule(toRemoveComputable.compute());
for (Map.Entry<Module, Collection<ExportableOrderEntry>> entry : byModule.entrySet()) {
Module module = entry.getKey();
Collection<ExportableOrderEntry> depsToRemove = entry.getValue();
ModifiableRootModel model = platformFacade.getModuleModifiableModel(module);
removeData(depsToRemove, model);
models.add(model);
}
ExternalSystemApiUtil.commitModels(synchronous, project, models);
}
catch (Throwable t) {
ExternalSystemApiUtil.disposeModels(models);
ExceptionUtil.rethrowUnchecked(t);
}
}
@@ -159,41 +172,25 @@ public abstract class AbstractDependencyDataService<E extends AbstractDependency
}
protected void removeData(@NotNull Collection<? extends ExportableOrderEntry> toRemove,
@NotNull final Module module,
@NotNull final PlatformFacade platformFacade,
boolean synchronous) {
if (toRemove.isEmpty()) {
return;
}
for (final ExportableOrderEntry dependency : toRemove) {
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(dependency.getOwnerModule()) {
@Override
public void execute() {
final ModifiableRootModel moduleRootModel = platformFacade.getModuleModifiableModel(module);
try {
// The thing is that intellij created order entry objects every time new modifiable model is created,
// that's why we can't use target dependency object as is but need to get a reference to the current
// entry object from the model instead.
for (OrderEntry entry : moduleRootModel.getOrderEntries()) {
if (entry instanceof ExportableOrderEntry) {
ExportableOrderEntry orderEntry = (ExportableOrderEntry)entry;
if (orderEntry.getPresentableName().equals(dependency.getPresentableName()) &&
orderEntry.getScope().equals(dependency.getScope())) {
moduleRootModel.removeOrderEntry(entry);
break;
}
}
else if (entry.getPresentableName().equals(dependency.getPresentableName())) {
moduleRootModel.removeOrderEntry(entry);
break;
}
}
}
finally {
moduleRootModel.commit();
@NotNull ModifiableRootModel moduleRootModel) {
for (ExportableOrderEntry dependency : toRemove) {
// The thing is that intellij created order entry objects every time new modifiable model is created,
// that's why we can't use target dependency object as is but need to get a reference to the current
// entry object from the model instead.
for (OrderEntry entry : moduleRootModel.getOrderEntries()) {
if (entry instanceof ExportableOrderEntry) {
ExportableOrderEntry orderEntry = (ExportableOrderEntry)entry;
if (orderEntry.getPresentableName().equals(dependency.getPresentableName()) &&
orderEntry.getScope().equals(dependency.getScope())) {
moduleRootModel.removeOrderEntry(entry);
break;
}
}
});
else if (entry.getPresentableName().equals(dependency.getPresentableName())) {
moduleRootModel.removeOrderEntry(entry);
break;
}
}
}
}
}
@@ -28,7 +28,6 @@ import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.externalSystem.util.Order;
@@ -36,7 +35,6 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.roots.SourceFolder;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
@@ -44,7 +42,7 @@ import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
@@ -78,94 +76,95 @@ public class ContentRootDataService extends AbstractProjectDataService<ContentRo
}
@Override
public void importData(@NotNull final Collection<DataNode<ContentRootData>> toImport,
public void importData(@NotNull Collection<DataNode<ContentRootData>> toImport,
@Nullable ProjectData projectData,
@NotNull final Project project,
@NotNull final PlatformFacade platformFacade,
final boolean synchronous) {
@NotNull Project project,
@NotNull PlatformFacade platformFacade,
boolean synchronous) {
if (toImport.isEmpty()) {
return;
}
MultiMap<DataNode<ModuleData>, DataNode<ContentRootData>> byModule = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.MODULE);
for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<ContentRootData>>> entry : byModule.entrySet()) {
final Module module = platformFacade.findIdeModule(entry.getKey().getData(), project);
if (module == null) {
LOG.warn(String.format(
"Can't import content roots. Reason: target module (%s) is not found at the ide. Content roots: %s",
entry.getKey(), entry.getValue()
));
continue;
List<ModifiableRootModel> models = ContainerUtilRt.newArrayList();
try {
MultiMap<DataNode<ModuleData>, DataNode<ContentRootData>> byModule = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.MODULE);
for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<ContentRootData>>> entry : byModule.entrySet()) {
Module module = platformFacade.findIdeModule(entry.getKey().getData(), project);
if (module == null) {
LOG.warn(String.format(
"Can't import content roots. Reason: target module (%s) is not found at the ide. Content roots: %s",
entry.getKey(), entry.getValue()
));
continue;
}
models.add(importData(entry.getValue(), module, platformFacade));
}
importData(entry.getValue(), module, synchronous);
ExternalSystemApiUtil.commitModels(synchronous, project, models);
}
catch (Throwable t) {
ExternalSystemApiUtil.disposeModels(models);
ExceptionUtil.rethrowUnchecked(t);
}
}
private static void importData(@NotNull final Collection<DataNode<ContentRootData>> data,
@NotNull final Module module,
boolean synchronous) {
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(module) {
@Override
public void execute() {
ModuleRootModificationUtil.updateModel(module, new Consumer<ModifiableRootModel>() {
@Override
public void consume(ModifiableRootModel model) {
final ContentEntry[] contentEntries = model.getContentEntries();
final Map<String, ContentEntry> contentEntriesMap = ContainerUtilRt.newHashMap();
for(ContentEntry contentEntry : contentEntries) {
contentEntriesMap.put(contentEntry.getUrl(), contentEntry);
}
@NotNull
private static ModifiableRootModel importData(@NotNull Collection<DataNode<ContentRootData>> data,
@NotNull Module module,
@NotNull PlatformFacade platformFacade) {
ModifiableRootModel model = platformFacade.getModuleModifiableModel(module);
ContentEntry[] contentEntries = model.getContentEntries();
Map<String, ContentEntry> contentEntriesMap = ContainerUtilRt.newHashMap();
for(ContentEntry contentEntry : contentEntries) {
contentEntriesMap.put(contentEntry.getUrl(), contentEntry);
}
boolean createEmptyContentRootDirectories = false;
if (!data.isEmpty()) {
ProjectSystemId projectSystemId = data.iterator().next().getData().getOwner();
AbstractExternalSystemSettings externalSystemSettings =
ExternalSystemApiUtil.getSettings(module.getProject(), projectSystemId);
boolean createEmptyContentRootDirectories = false;
if (!data.isEmpty()) {
ProjectSystemId projectSystemId = data.iterator().next().getData().getOwner();
AbstractExternalSystemSettings externalSystemSettings =
ExternalSystemApiUtil.getSettings(module.getProject(), projectSystemId);
String path = module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY);
if (path != null) {
ExternalProjectSettings projectSettings = externalSystemSettings.getLinkedProjectSettings(path);
createEmptyContentRootDirectories = projectSettings != null && projectSettings.isCreateEmptyContentRootDirectories();
}
}
for (final DataNode<ContentRootData> node : data) {
final ContentRootData contentRoot = node.getData();
final ContentEntry contentEntry = findOrCreateContentRoot(model, contentRoot.getRootPath());
contentEntry.clearExcludeFolders();
contentEntry.clearSourceFolders();
LOG.debug(String.format("Importing content root '%s' for module '%s'", contentRoot.getRootPath(), module.getName()));
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.SOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.SOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.TEST_SOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.RESOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaResourceRootType.RESOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST_RESOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaResourceRootType.TEST_RESOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.SOURCE_GENERATED)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.SOURCE, true, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST_GENERATED)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.TEST_SOURCE, true, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.EXCLUDED)) {
createExcludedRootIfAbsent(contentEntry, path, module.getName(), module.getProject());
}
contentEntriesMap.remove(contentEntry.getUrl());
}
for(ContentEntry contentEntry : contentEntriesMap.values()) {
model.removeContentEntry(contentEntry);
}
}
});
String path = module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY);
if (path != null) {
ExternalProjectSettings projectSettings = externalSystemSettings.getLinkedProjectSettings(path);
createEmptyContentRootDirectories = projectSettings != null && projectSettings.isCreateEmptyContentRootDirectories();
}
});
}
for (DataNode<ContentRootData> node : data) {
ContentRootData contentRoot = node.getData();
ContentEntry contentEntry = findOrCreateContentRoot(model, contentRoot.getRootPath());
contentEntry.clearExcludeFolders();
contentEntry.clearSourceFolders();
LOG.debug(String.format("Importing content root '%s' for module '%s'", contentRoot.getRootPath(), module.getName()));
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.SOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.SOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.TEST_SOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.RESOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaResourceRootType.RESOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST_RESOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaResourceRootType.TEST_RESOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.SOURCE_GENERATED)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.SOURCE, true, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST_GENERATED)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.TEST_SOURCE, true, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.EXCLUDED)) {
createExcludedRootIfAbsent(contentEntry, path, module.getName(), module.getProject());
}
contentEntriesMap.remove(contentEntry.getUrl());
}
for(ContentEntry contentEntry : contentEntriesMap.values()) {
model.removeContentEntry(contentEntry);
}
return model;
}
@NotNull
@@ -1,14 +1,12 @@
package com.intellij.openapi.externalSystem.service.project.manage;
import com.intellij.ide.highlighter.ArchiveFileType;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.Key;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.project.LibraryData;
import com.intellij.openapi.externalSystem.model.project.LibraryPathType;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.service.project.ExternalLibraryPathTypeMapper;
import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
@@ -68,18 +66,23 @@ public class LibraryDataService extends AbstractProjectDataService<LibraryData,
@Override
public void importData(@NotNull final Collection<DataNode<LibraryData>> toImport,
@Nullable final ProjectData projectData,
@Nullable ProjectData projectData,
@NotNull final Project project,
@NotNull final PlatformFacade platformFacade,
final boolean synchronous) {
for (DataNode<LibraryData> dataNode : toImport) {
importLibrary(dataNode.getData(), project, platformFacade, synchronous);
}
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
for (DataNode<LibraryData> dataNode : toImport) {
importLibrary(dataNode.getData(), project, platformFacade, synchronous);
}
}
});
}
private void importLibrary(@NotNull final LibraryData toImport,
@NotNull final Project project,
@NotNull final PlatformFacade platformFacade,
private void importLibrary(@NotNull LibraryData toImport,
@NotNull Project project,
@NotNull PlatformFacade platformFacade,
boolean synchronous) {
Map<OrderRootType, Collection<File>> libraryFiles = prepareLibraryFiles(toImport);
@@ -88,14 +91,14 @@ public class LibraryDataService extends AbstractProjectDataService<LibraryData,
syncPaths(toImport, library, project, synchronous);
return;
}
importLibrary(toImport.getInternalName(), libraryFiles, project, platformFacade, synchronous);
importLibrary(toImport.getInternalName(), libraryFiles, project, platformFacade);
}
@NotNull
public Map<OrderRootType, Collection<File>> prepareLibraryFiles(@NotNull LibraryData data) {
Map<OrderRootType, Collection<File>> result = ContainerUtilRt.newHashMap();
for (LibraryPathType pathType : LibraryPathType.values()) {
final Set<String> paths = data.getPaths(pathType);
Set<String> paths = data.getPaths(pathType);
if (paths.isEmpty()) {
continue;
}
@@ -104,38 +107,32 @@ public class LibraryDataService extends AbstractProjectDataService<LibraryData,
return result;
}
private void importLibrary(@NotNull final String libraryName,
@NotNull final Map<OrderRootType, Collection<File>> libraryFiles,
@NotNull final Project project,
@NotNull final PlatformFacade platformFacade,
boolean synchronous)
private void importLibrary(@NotNull String libraryName,
@NotNull Map<OrderRootType, Collection<File>> libraryFiles,
@NotNull Project project,
@NotNull PlatformFacade platformFacade)
{
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
// Is assumed to be called from the EDT.
final LibraryTable libraryTable = platformFacade.getProjectLibraryTable(project);
final LibraryTable.ModifiableModel projectLibraryModel = libraryTable.getModifiableModel();
final Library intellijLibrary;
try {
intellijLibrary = projectLibraryModel.createLibrary(libraryName);
}
finally {
projectLibraryModel.commit();
}
final Library.ModifiableModel libraryModel = intellijLibrary.getModifiableModel();
try {
registerPaths(libraryFiles, libraryModel, libraryName);
}
finally {
libraryModel.commit();
}
}
});
// Is assumed to be called from the EDT.
LibraryTable libraryTable = platformFacade.getProjectLibraryTable(project);
LibraryTable.ModifiableModel projectLibraryModel = libraryTable.getModifiableModel();
Library intellijLibrary;
try {
intellijLibrary = projectLibraryModel.createLibrary(libraryName);
}
finally {
projectLibraryModel.commit();
}
Library.ModifiableModel libraryModel = intellijLibrary.getModifiableModel();
try {
registerPaths(libraryFiles, libraryModel, libraryName);
}
finally {
libraryModel.commit();
}
}
@SuppressWarnings("MethodMayBeStatic")
public void registerPaths(@NotNull final Map<OrderRootType, Collection<File>> libraryFiles,
public void registerPaths(@NotNull Map<OrderRootType, Collection<File>> libraryFiles,
@NotNull Library.ModifiableModel model,
@NotNull String libraryName)
{
@@ -150,14 +147,14 @@ public class LibraryDataService extends AbstractProjectDataService<LibraryData,
}
String url = VfsUtil.getUrlForLibraryRoot(file);
final String[] urls = model.getUrls(entry.getKey());
String[] urls = model.getUrls(entry.getKey());
if (!ArrayUtil.contains(url, urls)) {
model.addRoot(url, entry.getKey());
}
continue;
}
if (virtualFile.isDirectory()) {
final VirtualFile[] files = model.getFiles(entry.getKey());
VirtualFile[] files = model.getFiles(entry.getKey());
if (!ArrayUtil.contains(virtualFile, files)) {
model.addRoot(virtualFile, entry.getKey());
}
@@ -173,7 +170,7 @@ public class LibraryDataService extends AbstractProjectDataService<LibraryData,
continue;
}
}
final VirtualFile[] files = model.getFiles(entry.getKey());
VirtualFile[] files = model.getFiles(entry.getKey());
if (!ArrayUtil.contains(root, files)) {
model.addRoot(root, entry.getKey());
}
@@ -216,12 +213,12 @@ public class LibraryDataService extends AbstractProjectDataService<LibraryData,
});
}
public void syncPaths(@NotNull final LibraryData externalLibrary, @NotNull final Library ideLibrary, @NotNull final Project project, boolean synchronous) {
public void syncPaths(@NotNull LibraryData externalLibrary, @NotNull Library ideLibrary, @NotNull Project project, boolean synchronous) {
if (externalLibrary.isUnresolved()) {
return;
}
final Map<OrderRootType, Set<String>> toRemove = ContainerUtilRt.newHashMap();
final Map<OrderRootType, Set<String>> toAdd = ContainerUtilRt.newHashMap();
Map<OrderRootType, Set<String>> toRemove = ContainerUtilRt.newHashMap();
Map<OrderRootType, Set<String>> toAdd = ContainerUtilRt.newHashMap();
for (LibraryPathType pathType : LibraryPathType.values()) {
OrderRootType ideType = myLibraryPathTypeMapper.map(pathType);
HashSet<String> toAddPerType = ContainerUtilRt.newHashSet(externalLibrary.getPaths(pathType));
@@ -240,27 +237,22 @@ public class LibraryDataService extends AbstractProjectDataService<LibraryData,
if (toRemove.isEmpty() && toAdd.isEmpty()) {
return;
}
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
Library.ModifiableModel model = ideLibrary.getModifiableModel();
try {
for (Map.Entry<OrderRootType, Set<String>> entry : toRemove.entrySet()) {
for (String path : entry.getValue()) {
model.removeRoot(path, entry.getKey());
}
}
for (Map.Entry<OrderRootType, Set<String>> entry : toAdd.entrySet()) {
Map<OrderRootType, Collection<File>> roots = ContainerUtilRt.newHashMap();
roots.put(entry.getKey(), ContainerUtil.map(entry.getValue(), PATH_TO_FILE));
registerPaths(roots, model, externalLibrary.getInternalName());
}
}
finally {
model.commit();
Library.ModifiableModel model = ideLibrary.getModifiableModel();
try {
for (Map.Entry<OrderRootType, Set<String>> entry : toRemove.entrySet()) {
for (String path : entry.getValue()) {
model.removeRoot(path, entry.getKey());
}
}
});
for (Map.Entry<OrderRootType, Set<String>> entry : toAdd.entrySet()) {
Map<OrderRootType, Collection<File>> roots = ContainerUtilRt.newHashMap();
roots.put(entry.getKey(), ContainerUtil.map(entry.getValue(), PATH_TO_FILE));
registerPaths(roots, model, externalLibrary.getInternalName());
}
}
finally {
model.commit();
}
}
}
@@ -21,7 +21,6 @@ import com.intellij.openapi.externalSystem.model.Key;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.project.*;
import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.externalSystem.util.Order;
@@ -35,6 +34,8 @@ import com.intellij.openapi.roots.impl.ModuleLibraryOrderEntryImpl;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
@@ -42,6 +43,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -78,17 +80,28 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
return;
}
MultiMap<DataNode<ModuleData>, DataNode<LibraryDependencyData>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<LibraryDependencyData>>> entry : byModule.entrySet()) {
Module module = platformFacade.findIdeModule(entry.getKey().getData(), project);
if (module == null) {
LOG.warn(String.format(
"Can't import library dependencies %s. Reason: target module (%s) is not found at the ide and can't be imported",
entry.getValue(), entry.getKey()
));
continue;
MyImporter importer = new MyImporter(platformFacade);
try {
MultiMap<DataNode<ModuleData>, DataNode<LibraryDependencyData>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<LibraryDependencyData>>> entry : byModule.entrySet()) {
Module module = platformFacade.findIdeModule(entry.getKey().getData(), project);
Collection<DataNode<LibraryDependencyData>> libraryDependency = entry.getValue();
if (module == null) {
LOG.warn(String.format(
"Can't import library dependencies %s. Reason: target module (%s) is not found at the ide and can't be imported",
libraryDependency, entry.getKey()
));
continue;
}
importer.importData(module, libraryDependency);
}
importData(entry.getValue(), module, platformFacade, synchronous);
// change libraries first
ExternalSystemApiUtil.commitChangedModels(synchronous, project, importer.getLibraryModels());
ExternalSystemApiUtil.commitModels(synchronous, project, importer.getModels());
}
catch (Throwable t) {
ExternalSystemApiUtil.disposeModels(importer.getModels());
ExceptionUtil.rethrowUnchecked(t);
}
}
@@ -103,158 +116,157 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
return orderEntry.getLibraryName();
}
private void importData(@NotNull final Collection<DataNode<LibraryDependencyData>> nodesToImport,
@NotNull final Module module,
@NotNull final PlatformFacade platformFacade,
final boolean synchronous) {
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(module) {
@Override
public void execute() {
// The general idea is to import all external project library dependencies and module libraries which don't present at the
// ide side yet and remove all project library dependencies and module libraries which present at the ide but not at
// the given collection.
// The trick is that we should perform module settings modification inside try/finally block against target root model.
// That means that we need to prepare all necessary data, obtain a model and modify it as necessary.
Map<Set<String>/* library paths */, LibraryDependencyData> moduleLibrariesToImport = ContainerUtilRt.newHashMap();
Map<String/* library name + scope */, LibraryDependencyData> projectLibrariesToImport = ContainerUtilRt.newHashMap();
Set<LibraryDependencyData> toImport = ContainerUtilRt.newLinkedHashSet();
private class MyImporter {
private final PlatformFacade myPlatformFacade;
private final List<ModifiableRootModel> myModels = ContainerUtilRt.newArrayList();
private final List<Library.ModifiableModel> myLibraryModels = ContainerUtilRt.newArrayList();
boolean hasUnresolved = false;
for (DataNode<LibraryDependencyData> dependencyNode : nodesToImport) {
LibraryDependencyData dependencyData = dependencyNode.getData();
LibraryData libraryData = dependencyData.getTarget();
hasUnresolved |= libraryData.isUnresolved();
switch (dependencyData.getLevel()) {
case MODULE:
if (!libraryData.isUnresolved()) {
Set<String> paths = ContainerUtilRt.newHashSet();
for (String path : libraryData.getPaths(LibraryPathType.BINARY)) {
paths.add(ExternalSystemApiUtil.toCanonicalPath(path) + dependencyData.getScope().name());
}
moduleLibrariesToImport.put(paths, dependencyData);
toImport.add(dependencyData);
private MyImporter(PlatformFacade platformFacade) {
myPlatformFacade = platformFacade;
}
public List<ModifiableRootModel> getModels() {
return ContainerUtil.newUnmodifiableList(myModels);
}
public List<Library.ModifiableModel> getLibraryModels() {
return ContainerUtil.newUnmodifiableList(myLibraryModels);
}
public void importData(@NotNull Module module, @NotNull Collection<DataNode<LibraryDependencyData>> nodesToImport) {
// The general idea is to import all external project library dependencies and module libraries which don't present at the
// ide side yet and remove all project library dependencies and module libraries which present at the ide but not at
// the given collection.
// The trick is that we should perform module settings modification inside try/finally block against target root model.
// That means that we need to prepare all necessary data, obtain a model and modify it as necessary.
Map<Set<String>/* library paths */, LibraryDependencyData> moduleLibrariesToImport = ContainerUtilRt.newHashMap();
Map<String/* library name + scope */, LibraryDependencyData> projectLibrariesToImport = ContainerUtilRt.newHashMap();
Set<LibraryDependencyData> toImport = ContainerUtilRt.newLinkedHashSet();
boolean hasUnresolved = false;
for (DataNode<LibraryDependencyData> dependencyNode : nodesToImport) {
LibraryDependencyData dependencyData = dependencyNode.getData();
LibraryData libraryData = dependencyData.getTarget();
hasUnresolved |= libraryData.isUnresolved();
switch (dependencyData.getLevel()) {
case MODULE:
if (!libraryData.isUnresolved()) {
Set<String> paths = ContainerUtilRt.newHashSet();
for (String path : libraryData.getPaths(LibraryPathType.BINARY)) {
paths.add(ExternalSystemApiUtil.toCanonicalPath(path) + dependencyData.getScope().name());
}
break;
case PROJECT:
projectLibrariesToImport.put(libraryData.getInternalName() + dependencyData.getScope().name(), dependencyData);
moduleLibrariesToImport.put(paths, dependencyData);
toImport.add(dependencyData);
}
}
final ModifiableRootModel moduleRootModel = platformFacade.getModuleModifiableModel(module);
LibraryTable moduleLibraryTable = moduleRootModel.getModuleLibraryTable();
LibraryTable libraryTable = platformFacade.getProjectLibraryTable(module.getProject());
try {
syncExistingAndRemoveObsolete(moduleLibrariesToImport, projectLibrariesToImport, toImport, moduleRootModel, hasUnresolved);
// Import missing library dependencies.
if (!toImport.isEmpty()) {
importMissing(toImport, moduleRootModel, moduleLibraryTable, libraryTable, module);
}
}
finally {
moduleRootModel.commit();
}
}
});
}
private void importMissing(@NotNull Set<LibraryDependencyData> toImport,
@NotNull ModifiableRootModel moduleRootModel,
@NotNull LibraryTable moduleLibraryTable,
@NotNull LibraryTable libraryTable,
@NotNull Module module) {
for (final LibraryDependencyData dependencyData : toImport) {
final LibraryData libraryData = dependencyData.getTarget();
final String libraryName = libraryData.getInternalName();
switch (dependencyData.getLevel()) {
case MODULE:
final Library moduleLib = moduleLibraryTable.createLibrary(libraryName);
syncExistingLibraryDependency(dependencyData, moduleLib, moduleRootModel, module);
break;
case PROJECT:
final Library projectLib = libraryTable.getLibraryByName(libraryName);
if (projectLib == null) {
syncExistingLibraryDependency(dependencyData, moduleLibraryTable.createLibrary(libraryName), moduleRootModel, module);
}
break;
case PROJECT:
projectLibrariesToImport.put(libraryData.getInternalName() + dependencyData.getScope().name(), dependencyData);
toImport.add(dependencyData);
}
}
ModifiableRootModel moduleRootModel = myPlatformFacade.getModuleModifiableModel(module);
LibraryTable moduleLibraryTable = moduleRootModel.getModuleLibraryTable();
LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(module.getProject());
syncExistingAndRemoveObsolete(moduleLibrariesToImport, projectLibrariesToImport, toImport, moduleRootModel, hasUnresolved);
// Import missing library dependencies.
if (!toImport.isEmpty()) {
importMissing(toImport, moduleRootModel, moduleLibraryTable, libraryTable, module);
}
myModels.add(moduleRootModel);
}
private void importMissing(@NotNull Set<LibraryDependencyData> toImport,
@NotNull ModifiableRootModel moduleRootModel,
@NotNull LibraryTable moduleLibraryTable,
@NotNull LibraryTable libraryTable,
@NotNull Module module) {
for (LibraryDependencyData dependencyData : toImport) {
LibraryData libraryData = dependencyData.getTarget();
String libraryName = libraryData.getInternalName();
switch (dependencyData.getLevel()) {
case MODULE:
Library moduleLib = moduleLibraryTable.createLibrary(libraryName);
syncExistingLibraryDependency(dependencyData, moduleLib, moduleRootModel, module);
break;
case PROJECT:
Library projectLib = libraryTable.getLibraryByName(libraryName);
if (projectLib == null) {
syncExistingLibraryDependency(dependencyData, moduleLibraryTable.createLibrary(libraryName), moduleRootModel, module);
break;
}
LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(projectLib);
setLibraryScope(orderEntry, projectLib, module, dependencyData);
}
}
}
private void setLibraryScope(@NotNull LibraryOrderEntry orderEntry,
@NotNull Library lib,
@NotNull Module module,
@NotNull LibraryDependencyData dependencyData) {
LOG.debug(String.format("Adding library dependency '%s' to module '%s'", lib.getName(), module.getName()));
orderEntry.setExported(dependencyData.isExported());
orderEntry.setScope(dependencyData.getScope());
LOG.debug(String.format("Configuring library dependency '%s' of module '%s' to be%s exported and have scope %s", lib.getName(), module.getName(), dependencyData.isExported() ? " not" : "", dependencyData.getScope()));
}
private void syncExistingAndRemoveObsolete(@NotNull Map<Set<String>, LibraryDependencyData> moduleLibrariesToImport,
@NotNull Map<String, LibraryDependencyData> projectLibrariesToImport,
@NotNull Set<LibraryDependencyData> toImport,
@NotNull ModifiableRootModel moduleRootModel,
boolean hasUnresolvedLibraries) {
Set<String> moduleLibraryKey = ContainerUtilRt.newHashSet();
for (OrderEntry entry : moduleRootModel.getOrderEntries()) {
if (entry instanceof ModuleLibraryOrderEntryImpl) {
ModuleLibraryOrderEntryImpl moduleLibraryOrderEntry = (ModuleLibraryOrderEntryImpl)entry;
Library library = moduleLibraryOrderEntry.getLibrary();
if (library == null) {
LOG.warn("Skipping module-level library entry because it doesn't have backing Library object. Entry: " + entry);
continue;
}
moduleLibraryKey.clear();
for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) {
moduleLibraryKey.add(ExternalSystemApiUtil.getLocalFileSystemPath(file) + moduleLibraryOrderEntry.getScope().name());
}
LibraryDependencyData existing = moduleLibrariesToImport.remove(moduleLibraryKey);
if (existing == null) {
moduleRootModel.removeOrderEntry(entry);
}
else {
syncExistingLibraryDependency(existing, library, moduleRootModel, moduleLibraryOrderEntry.getOwnerModule());
toImport.remove(existing);
}
LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(projectLib);
setLibraryScope(orderEntry, projectLib, module, dependencyData);
}
}
}
private static void setLibraryScope(@NotNull LibraryOrderEntry orderEntry,
@NotNull Library lib,
@NotNull Module module,
@NotNull LibraryDependencyData dependencyData) {
LOG.debug(String.format("Adding library dependency '%s' to module '%s'", lib.getName(), module.getName()));
orderEntry.setExported(dependencyData.isExported());
orderEntry.setScope(dependencyData.getScope());
LOG.debug(String.format(
"Configuring library dependency '%s' of module '%s' to be%s exported and have scope %s",
lib.getName(), module.getName(), dependencyData.isExported() ? " not" : "", dependencyData.getScope()
));
}
private void syncExistingAndRemoveObsolete(@NotNull Map<Set<String>, LibraryDependencyData> moduleLibrariesToImport,
@NotNull Map<String, LibraryDependencyData> projectLibrariesToImport,
@NotNull Set<LibraryDependencyData> toImport,
@NotNull ModifiableRootModel moduleRootModel,
boolean hasUnresolvedLibraries) {
Set<String> moduleLibraryKey = ContainerUtilRt.newHashSet();
for (OrderEntry entry : moduleRootModel.getOrderEntries()) {
if (entry instanceof ModuleLibraryOrderEntryImpl) {
ModuleLibraryOrderEntryImpl moduleLibraryOrderEntry = (ModuleLibraryOrderEntryImpl)entry;
Library library = moduleLibraryOrderEntry.getLibrary();
if (library == null) {
LOG.warn("Skipping module-level library entry because it doesn't have backing Library object. Entry: " + entry);
continue;
}
moduleLibraryKey.clear();
for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) {
moduleLibraryKey.add(ExternalSystemApiUtil.getLocalFileSystemPath(file) + moduleLibraryOrderEntry.getScope().name());
}
LibraryDependencyData existing = moduleLibrariesToImport.remove(moduleLibraryKey);
if (existing == null) {
moduleRootModel.removeOrderEntry(entry);
}
else {
syncExistingLibraryDependency(existing, library, moduleRootModel, moduleLibraryOrderEntry.getOwnerModule());
toImport.remove(existing);
}
}
else if (entry instanceof LibraryOrderEntry) {
final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry;
final String libraryName = libraryOrderEntry.getLibraryName();
final LibraryDependencyData existing = projectLibrariesToImport.remove(libraryName + libraryOrderEntry.getScope().name());
if (existing != null) {
toImport.remove(existing);
}
else if (!hasUnresolvedLibraries) {
// There is a possible case that a project has been successfully imported from external model and after
// that network/repo goes down. We don't want to drop existing binary mappings then.
moduleRootModel.removeOrderEntry(entry);
else if (entry instanceof LibraryOrderEntry) {
LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry;
String libraryName = libraryOrderEntry.getLibraryName();
LibraryDependencyData existing = projectLibrariesToImport.remove(libraryName + libraryOrderEntry.getScope().name());
if (existing != null) {
toImport.remove(existing);
}
else if (!hasUnresolvedLibraries) {
// There is a possible case that a project has been successfully imported from external model and after
// that network/repo goes down. We don't want to drop existing binary mappings then.
moduleRootModel.removeOrderEntry(entry);
}
}
}
}
}
private void syncExistingLibraryDependency(@NotNull LibraryDependencyData libraryDependencyData,
@NotNull Library library,
@NotNull ModifiableRootModel moduleRootModel,
@NotNull Module module) {
final Library.ModifiableModel libModel = library.getModifiableModel();
try {
final String libraryName = libraryDependencyData.getInternalName();
private void syncExistingLibraryDependency(@NotNull LibraryDependencyData libraryDependencyData,
@NotNull Library library,
@NotNull ModifiableRootModel moduleRootModel,
@NotNull Module module) {
Library.ModifiableModel libModel = library.getModifiableModel();
String libraryName = libraryDependencyData.getInternalName();
Map<OrderRootType, Collection<File>> files = myLibraryManager.prepareLibraryFiles(libraryDependencyData.getTarget());
myLibraryManager.registerPaths(files, libModel, libraryName);
LibraryOrderEntry orderEntry = moduleRootModel.findLibraryOrderEntry(library);
assert orderEntry != null;
setLibraryScope(orderEntry, library, module, libraryDependencyData);
}
finally {
libModel.commit();
myLibraryModels.add(libModel);
}
}
}
@@ -15,7 +15,6 @@
*/
package com.intellij.openapi.externalSystem.service.project.manage;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.DataNode;
@@ -39,6 +38,7 @@ import com.intellij.ui.CheckBoxList;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.Consumer;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.Function;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
@@ -52,11 +52,10 @@ import java.awt.*;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Encapsulates functionality of importing external system module to the intellij project.
*
*
* @author Denis Zhdanov
* @since 2/7/12 2:49 PM
*/
@@ -74,82 +73,72 @@ public class ModuleDataService extends AbstractProjectDataService<ModuleData, Mo
}
@Override
public void importData(@NotNull final Collection<DataNode<ModuleData>> toImport,
public void importData(@NotNull Collection<DataNode<ModuleData>> toImport,
@Nullable ProjectData projectData,
@NotNull final Project project,
@NotNull final PlatformFacade platformFacade,
final boolean synchronous) {
@NotNull Project project,
@NotNull PlatformFacade platformFacade,
boolean synchronous) {
if (toImport.isEmpty()) {
return;
}
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
final Collection<DataNode<ModuleData>> toCreate = filterExistingModules(toImport, project, platformFacade);
if (!toCreate.isEmpty()) {
createModules(toCreate, project, platformFacade);
}
for (DataNode<ModuleData> node : toImport) {
Module module = platformFacade.findIdeModule(node.getData(), project);
if (module != null) {
syncPaths(module, platformFacade, node.getData());
}
}
}
});
Collection<DataNode<ModuleData>> toCreate = filterExistingModules(toImport, project, platformFacade);
if (!toCreate.isEmpty()) {
ExternalSystemApiUtil.commitModels(synchronous, project, createModules(project, platformFacade, toCreate));
}
ExternalSystemApiUtil.commitModels(synchronous, project, syncModulesPaths(project, platformFacade, toImport));
}
private static void createModules(@NotNull final Collection<DataNode<ModuleData>> toCreate,
@NotNull final Project project,
@NotNull final PlatformFacade platformFacade) {
Application application = ApplicationManager.getApplication();
final Map<DataNode<ModuleData>, Module> moduleMappings = ContainerUtilRt.newHashMap();
application.runWriteAction(new Runnable() {
@NotNull
private static List<ModifiableRootModel> createModules(@NotNull Project project,
@NotNull PlatformFacade platformFacade,
Collection<DataNode<ModuleData>> toCreate) {
List<ModifiableRootModel> models = ContainerUtilRt.newArrayList();
try {
for (DataNode<ModuleData> moduleData : toCreate) {
models.add(createModule(project, platformFacade, moduleData));
}
}
catch (Throwable t) {
ExternalSystemApiUtil.disposeModels(models);
ExceptionUtil.rethrowUnchecked(t);
}
return models;
}
private static ModifiableRootModel createModule(@NotNull Project project,
@NotNull PlatformFacade platformFacade,
@NotNull DataNode<ModuleData> module) {
ModuleData data = module.getData();
Module created = platformFacade.newModule(project, data.getModuleFilePath(), data.getModuleTypeId());
// Ensure that the dependencies are clear (used to be not clear when manually removing the module and importing it via gradle)
final ModifiableRootModel moduleRootModel = platformFacade.getModuleModifiableModel(created);
moduleRootModel.inheritSdk();
setModuleOptions(created, module);
RootPolicy<Object> visitor = new RootPolicy<Object>() {
@Override
public void run() {
for (DataNode<ModuleData> module : toCreate) {
importModule(module);
}
public Object visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Object value) {
moduleRootModel.removeOrderEntry(libraryOrderEntry);
return value;
}
private void importModule(@NotNull DataNode<ModuleData> module) {
ModuleData data = module.getData();
final Module created = platformFacade.newModule(project, data.getModuleFilePath(), data.getModuleTypeId());
// Ensure that the dependencies are clear (used to be not clear when manually removing the module and importing it via gradle)
final ModifiableRootModel moduleRootModel = platformFacade.getModuleModifiableModel(created);
moduleRootModel.inheritSdk();
setModuleOptions(created, module);
RootPolicy<Object> visitor = new RootPolicy<Object>() {
@Override
public Object visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Object value) {
moduleRootModel.removeOrderEntry(libraryOrderEntry);
return value;
}
@Override
public Object visitModuleOrderEntry(ModuleOrderEntry moduleOrderEntry, Object value) {
moduleRootModel.removeOrderEntry(moduleOrderEntry);
return value;
}
};
try {
for (OrderEntry orderEntry : moduleRootModel.getOrderEntries()) {
orderEntry.accept(visitor, null);
}
}
finally {
moduleRootModel.commit();
}
moduleMappings.put(module, created);
@Override
public Object visitModuleOrderEntry(ModuleOrderEntry moduleOrderEntry, Object value) {
moduleRootModel.removeOrderEntry(moduleOrderEntry);
return value;
}
});
};
for (OrderEntry orderEntry : moduleRootModel.getOrderEntries()) {
orderEntry.accept(visitor, null);
}
return moduleRootModel;
}
@NotNull
private static Collection<DataNode<ModuleData>> filterExistingModules(@NotNull Collection<DataNode<ModuleData>> modules,
@NotNull Project project, @NotNull PlatformFacade platformFacade)
@NotNull Project project,
@NotNull PlatformFacade platformFacade)
{
Collection<DataNode<ModuleData>> result = ContainerUtilRt.newArrayList();
for (DataNode<ModuleData> node : modules) {
@@ -165,30 +154,46 @@ public class ModuleDataService extends AbstractProjectDataService<ModuleData, Mo
return result;
}
private static void syncPaths(@NotNull Module module, @NotNull PlatformFacade platformFacade, @NotNull ModuleData data) {
@NotNull
private List<ModifiableRootModel> syncModulesPaths(@NotNull Project project,
@NotNull PlatformFacade platformFacade,
Collection<DataNode<ModuleData>> toCreate) {
List<ModifiableRootModel> models = ContainerUtilRt.newArrayList();
try {
for (DataNode<ModuleData> moduleData : toCreate) {
Module module = platformFacade.findIdeModule(moduleData.getData(), project);
if (module != null) {
models.add(syncPaths(module, platformFacade, moduleData.getData()));
}
}
}
catch (Throwable t) {
ExternalSystemApiUtil.disposeModels(models);
ExceptionUtil.rethrowUnchecked(t);
}
return models;
}
@NotNull
private static ModifiableRootModel syncPaths(@NotNull Module module, @NotNull PlatformFacade platformFacade, @NotNull ModuleData data) {
ModifiableRootModel modifiableModel = platformFacade.getModuleModifiableModel(module);
CompilerModuleExtension extension = modifiableModel.getModuleExtension(CompilerModuleExtension.class);
if (extension == null) {
modifiableModel.dispose();
LOG.warn(String.format("Can't sync paths for module '%s'. Reason: no compiler extension is found for it", module.getName()));
return;
return modifiableModel;
}
String compileOutputPath = data.getCompileOutputPath(ExternalSystemSourceType.SOURCE);
if (compileOutputPath != null) {
extension.setCompilerOutputPath(VfsUtilCore.pathToUrl(compileOutputPath));
}
try {
String compileOutputPath = data.getCompileOutputPath(ExternalSystemSourceType.SOURCE);
if (compileOutputPath != null) {
extension.setCompilerOutputPath(VfsUtilCore.pathToUrl(compileOutputPath));
}
String testCompileOutputPath = data.getCompileOutputPath(ExternalSystemSourceType.TEST);
if (testCompileOutputPath != null) {
extension.setCompilerOutputPathForTests(VfsUtilCore.pathToUrl(testCompileOutputPath));
}
String testCompileOutputPath = data.getCompileOutputPath(ExternalSystemSourceType.TEST);
if (testCompileOutputPath != null) {
extension.setCompilerOutputPathForTests(VfsUtilCore.pathToUrl(testCompileOutputPath));
}
extension.inheritCompilerOutputPath(data.isInheritProjectCompileOutputPath());
}
finally {
modifiableModel.commit();
}
extension.inheritCompilerOutputPath(data.isInheritProjectCompileOutputPath());
return modifiableModel;
}
@NotNull
@@ -340,7 +345,7 @@ public class ModuleDataService extends AbstractProjectDataService<ModuleData, Mo
protected JComponent createCenterPanel() {
return new JBScrollPane(content);
}
@NotNull
protected Action[] createActions() {
return new Action[]{getOKAction()};
@@ -377,7 +382,7 @@ public class ModuleDataService extends AbstractProjectDataService<ModuleData, Mo
module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY, moduleData.getOwner().toString());
module.setOption(ExternalSystemConstants.LINKED_PROJECT_ID_KEY, moduleData.getId());
module.setOption(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY, moduleData.getLinkedExternalProjectPath());
final ProjectData projectData = moduleDataNode.getData(ProjectKeys.PROJECT);
ProjectData projectData = moduleDataNode.getData(ProjectKeys.PROJECT);
module.setOption(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY, projectData != null ? projectData.getLinkedExternalProjectPath() : "");
if (moduleData.getGroup() != null) {
@@ -19,23 +19,18 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.Key;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.project.LibraryDependencyData;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.externalSystem.util.Order;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
@@ -68,17 +63,25 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
@NotNull Project project,
@NotNull PlatformFacade platformFacade,
boolean synchronous) {
MultiMap<DataNode<ModuleData>, DataNode<ModuleDependencyData>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<ModuleDependencyData>>> entry : byModule.entrySet()) {
Module ideModule = platformFacade.findIdeModule(entry.getKey().getData(), project);
if (ideModule == null) {
LOG.warn(String.format(
"Can't import module dependencies %s. Reason: target module (%s) is not found at the ide and can't be imported",
entry.getValue(), entry.getKey()
));
continue;
List<ModifiableRootModel> models = ContainerUtilRt.newArrayList();
try {
MultiMap<DataNode<ModuleData>, DataNode<ModuleDependencyData>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<ModuleDependencyData>>> entry : byModule.entrySet()) {
Module ideModule = platformFacade.findIdeModule(entry.getKey().getData(), project);
if (ideModule == null) {
LOG.warn(String.format(
"Can't import module dependencies %s. Reason: target module (%s) is not found at the ide and can't be imported",
entry.getValue(), entry.getKey()
));
continue;
}
models.add(importData(entry.getValue(), ideModule, platformFacade));
}
importData(entry.getValue(), ideModule, platformFacade, synchronous);
ExternalSystemApiUtil.commitModels(synchronous, project, models);
}
catch (Throwable t) {
ExternalSystemApiUtil.disposeModels(models);
ExceptionUtil.rethrowUnchecked(t);
}
}
@@ -93,62 +96,54 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
return orderEntry.getModuleName();
}
private void importData(@NotNull final Collection<DataNode<ModuleDependencyData>> toImport,
@NotNull final Module module,
@NotNull final PlatformFacade platformFacade,
final boolean synchronous)
@NotNull
private ModifiableRootModel importData(@NotNull Collection<DataNode<ModuleDependencyData>> toImport,
@NotNull Module module,
@NotNull PlatformFacade platformFacade)
{
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(module) {
@Override
public void execute() {
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
Map<Pair<String /* dependency module internal name */, /* dependency module scope */DependencyScope> , ModuleOrderEntry> toRemove = ContainerUtilRt.newHashMap();
for (OrderEntry entry : moduleRootManager.getOrderEntries()) {
if (entry instanceof ModuleOrderEntry) {
ModuleOrderEntry e = (ModuleOrderEntry)entry;
toRemove.put(Pair.create(e.getModuleName(), e.getScope()), e);
}
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
Map<Pair<String /* dependency module internal name */, /* dependency module scope */DependencyScope>, ModuleOrderEntry> toRemove =
ContainerUtilRt.newHashMap();
for (OrderEntry entry : moduleRootManager.getOrderEntries()) {
if (entry instanceof ModuleOrderEntry) {
ModuleOrderEntry e = (ModuleOrderEntry)entry;
toRemove.put(Pair.create(e.getModuleName(), e.getScope()), e);
}
}
ModifiableRootModel moduleRootModel = platformFacade.getModuleModifiableModel(module);
for (DataNode<ModuleDependencyData> dependencyNode : toImport) {
ModuleDependencyData dependencyData = dependencyNode.getData();
toRemove.remove(Pair.create(dependencyData.getInternalName(), dependencyData.getScope()));
String moduleName = dependencyData.getInternalName();
Module ideDependencyModule = platformFacade.findIdeModule(moduleName, module.getProject());
ModuleOrderEntry orderEntry;
if (module.equals(ideDependencyModule)) {
// skip recursive module dependency check
continue;
}
else {
if (ideDependencyModule == null) {
LOG.warn(String.format("Can't import module dependency for '%s' module. Reason: target module (%s) is not found at the ide",
module.getName(), dependencyData));
}
final ModifiableRootModel moduleRootModel = platformFacade.getModuleModifiableModel(module);
try {
for (DataNode<ModuleDependencyData> dependencyNode : toImport) {
final ModuleDependencyData dependencyData = dependencyNode.getData();
toRemove.remove(Pair.create(dependencyData.getInternalName(), dependencyData.getScope()));
final String moduleName = dependencyData.getInternalName();
Module ideDependencyModule = platformFacade.findIdeModule(moduleName, module.getProject());
ModuleOrderEntry orderEntry;
if (module.equals(ideDependencyModule)) {
// skip recursive module dependency check
continue;
} else {
if(ideDependencyModule == null) {
LOG.warn(String.format(
"Can't import module dependency for '%s' module. Reason: target module (%s) is not found at the ide",
module.getName(), dependencyData
));
}
orderEntry = platformFacade.findIdeModuleDependency(dependencyData, moduleRootModel);
if (orderEntry == null) {
orderEntry = ideDependencyModule == null
? moduleRootModel.addInvalidModuleEntry(moduleName)
: moduleRootModel.addModuleOrderEntry(ideDependencyModule);
}
}
orderEntry.setScope(dependencyData.getScope());
orderEntry.setExported(dependencyData.isExported());
}
}
finally {
moduleRootModel.commit();
}
if (!toRemove.isEmpty()) {
removeData(toRemove.values(), module, platformFacade, synchronous);
orderEntry = platformFacade.findIdeModuleDependency(dependencyData, moduleRootModel);
if (orderEntry == null) {
orderEntry = ideDependencyModule == null
? moduleRootModel.addInvalidModuleEntry(moduleName)
: moduleRootModel.addModuleOrderEntry(ideDependencyModule);
}
}
});
orderEntry.setScope(dependencyData.getScope());
orderEntry.setExported(dependencyData.isExported());
}
if (!toRemove.isEmpty()) {
removeData(toRemove.values(), moduleRootModel);
}
return moduleRootModel;
}
}
@@ -127,7 +127,7 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleCo
}
for (OrderEntry entry : ModuleRootManagerImpl.this.getOrderEntries()) {
assert !((RootModelComponentBase)entry).isDisposed();
assert !((RootModelComponentBase)entry).isDisposed() : String.format("%s is not disposed!", entry.getPresentableName());
}
}
};
@@ -589,7 +589,9 @@ public class LibraryImpl extends TraceableDisposable implements LibraryEx.Modifi
public void commit() {
checkDisposed();
mySource.commit(this);
if (isChanged()) {
mySource.commit(this);
}
Disposer.dispose(this);
}
@@ -167,6 +167,10 @@ public abstract class LibraryTableBase implements PersistentStateComponent<Eleme
private void commit(LibraryModel model) {
myFirstLoad = false;
ApplicationManager.getApplication().assertWriteAccessAllowed();
if (!model.isChanged()) {
myModel = model;
return;
}
//todo[nik] remove LibraryImpl#equals method instead of using identity sets
Set<Library> addedLibraries = ContainerUtil.newIdentityTroveSet(model.myLibraries);
addedLibraries.removeAll(myModel.myLibraries);