diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/PlatformFacade.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/PlatformFacade.java index 1ac0444b75a1..3f4f3801edfe 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/PlatformFacade.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/PlatformFacade.java @@ -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 diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java index fff82f6b3448..f457d5fb67e7 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java @@ -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 models) { + final List changedModels = ContainerUtil.findAll(models, new Condition() { + @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 models) { + for (ModifiableRootModel model : models) { + if (!model.isDisposed()) { + model.dispose(); + } + } + } + + public static void commitModels(boolean synchronous, Project project, List models) { + final List 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); } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java index 50373bf5d063..e98274ddca89 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java @@ -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> toRemoveComputable, - @NotNull final Collection> toIgnore, - @NotNull final ProjectData projectData, - @NotNull final Project project, - @NotNull final PlatformFacade platformFacade, - final boolean synchronous) { - Map> byModule = groupByModule(toRemoveComputable.compute()); - for (Map.Entry> entry : byModule.entrySet()) { - removeData(entry.getValue(), entry.getKey(), platformFacade, synchronous); + public void removeData(@NotNull Computable> toRemoveComputable, + @NotNull Collection> toIgnore, + @NotNull ProjectData projectData, + @NotNull Project project, + @NotNull PlatformFacade platformFacade, + boolean synchronous) { + List models = ContainerUtilRt.newArrayList(); + try { + Map> byModule = groupByModule(toRemoveComputable.compute()); + for (Map.Entry> entry : byModule.entrySet()) { + Module module = entry.getKey(); + Collection 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 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; + } + } } } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java index 0392bd7ac6c7..40691939614e 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java @@ -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> toImport, + public void importData(@NotNull Collection> 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> byModule = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.MODULE); - for (Map.Entry, Collection>> 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 models = ContainerUtilRt.newArrayList(); + try { + MultiMap, DataNode> byModule = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.MODULE); + for (Map.Entry, Collection>> 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> data, - @NotNull final Module module, - boolean synchronous) { - ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(module) { - @Override - public void execute() { - ModuleRootModificationUtil.updateModel(module, new Consumer() { - @Override - public void consume(ModifiableRootModel model) { - final ContentEntry[] contentEntries = model.getContentEntries(); - final Map contentEntriesMap = ContainerUtilRt.newHashMap(); - for(ContentEntry contentEntry : contentEntries) { - contentEntriesMap.put(contentEntry.getUrl(), contentEntry); - } + @NotNull + private static ModifiableRootModel importData(@NotNull Collection> data, + @NotNull Module module, + @NotNull PlatformFacade platformFacade) { + ModifiableRootModel model = platformFacade.getModuleModifiableModel(module); + ContentEntry[] contentEntries = model.getContentEntries(); + Map 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 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 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 diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java index ab16cfc045c4..24c4a94e0ebd 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java @@ -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> toImport, - @Nullable final ProjectData projectData, + @Nullable ProjectData projectData, @NotNull final Project project, @NotNull final PlatformFacade platformFacade, final boolean synchronous) { - for (DataNode dataNode : toImport) { - importLibrary(dataNode.getData(), project, platformFacade, synchronous); - } + ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) { + @Override + public void execute() { + for (DataNode 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> libraryFiles = prepareLibraryFiles(toImport); @@ -88,14 +91,14 @@ public class LibraryDataService extends AbstractProjectDataService> prepareLibraryFiles(@NotNull LibraryData data) { Map> result = ContainerUtilRt.newHashMap(); for (LibraryPathType pathType : LibraryPathType.values()) { - final Set paths = data.getPaths(pathType); + Set paths = data.getPaths(pathType); if (paths.isEmpty()) { continue; } @@ -104,38 +107,32 @@ public class LibraryDataService extends AbstractProjectDataService> libraryFiles, - @NotNull final Project project, - @NotNull final PlatformFacade platformFacade, - boolean synchronous) + private void importLibrary(@NotNull String libraryName, + @NotNull Map> 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> libraryFiles, + public void registerPaths(@NotNull Map> libraryFiles, @NotNull Library.ModifiableModel model, @NotNull String libraryName) { @@ -150,14 +147,14 @@ public class LibraryDataService extends AbstractProjectDataService> toRemove = ContainerUtilRt.newHashMap(); - final Map> toAdd = ContainerUtilRt.newHashMap(); + Map> toRemove = ContainerUtilRt.newHashMap(); + Map> toAdd = ContainerUtilRt.newHashMap(); for (LibraryPathType pathType : LibraryPathType.values()) { OrderRootType ideType = myLibraryPathTypeMapper.map(pathType); HashSet toAddPerType = ContainerUtilRt.newHashSet(externalLibrary.getPaths(pathType)); @@ -240,27 +237,22 @@ public class LibraryDataService extends AbstractProjectDataService> entry : toRemove.entrySet()) { - for (String path : entry.getValue()) { - model.removeRoot(path, entry.getKey()); - } - } - - for (Map.Entry> entry : toAdd.entrySet()) { - Map> 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> entry : toRemove.entrySet()) { + for (String path : entry.getValue()) { + model.removeRoot(path, entry.getKey()); } } - }); + + for (Map.Entry> entry : toAdd.entrySet()) { + Map> roots = ContainerUtilRt.newHashMap(); + roots.put(entry.getKey(), ContainerUtil.map(entry.getValue(), PATH_TO_FILE)); + registerPaths(roots, model, externalLibrary.getInternalName()); + } + } + finally { + model.commit(); + } } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDependencyDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDependencyDataService.java index 40f7ba1cdb85..5f5c700a8845 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDependencyDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDependencyDataService.java @@ -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> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE); - for (Map.Entry, Collection>> 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> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE); + for (Map.Entry, Collection>> entry : byModule.entrySet()) { + Module module = platformFacade.findIdeModule(entry.getKey().getData(), project); + Collection> 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> 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/* library paths */, LibraryDependencyData> moduleLibrariesToImport = ContainerUtilRt.newHashMap(); - Map projectLibrariesToImport = ContainerUtilRt.newHashMap(); - Set toImport = ContainerUtilRt.newLinkedHashSet(); + private class MyImporter { + private final PlatformFacade myPlatformFacade; + private final List myModels = ContainerUtilRt.newArrayList(); + private final List myLibraryModels = ContainerUtilRt.newArrayList(); - boolean hasUnresolved = false; - for (DataNode dependencyNode : nodesToImport) { - LibraryDependencyData dependencyData = dependencyNode.getData(); - LibraryData libraryData = dependencyData.getTarget(); - hasUnresolved |= libraryData.isUnresolved(); - switch (dependencyData.getLevel()) { - case MODULE: - if (!libraryData.isUnresolved()) { - Set 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 getModels() { + return ContainerUtil.newUnmodifiableList(myModels); + } + + public List getLibraryModels() { + return ContainerUtil.newUnmodifiableList(myLibraryModels); + } + + public void importData(@NotNull Module module, @NotNull Collection> 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/* library paths */, LibraryDependencyData> moduleLibrariesToImport = ContainerUtilRt.newHashMap(); + Map projectLibrariesToImport = ContainerUtilRt.newHashMap(); + Set toImport = ContainerUtilRt.newLinkedHashSet(); + + boolean hasUnresolved = false; + for (DataNode dependencyNode : nodesToImport) { + LibraryDependencyData dependencyData = dependencyNode.getData(); + LibraryData libraryData = dependencyData.getTarget(); + hasUnresolved |= libraryData.isUnresolved(); + switch (dependencyData.getLevel()) { + case MODULE: + if (!libraryData.isUnresolved()) { + Set 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 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 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, LibraryDependencyData> moduleLibrariesToImport, + @NotNull Map projectLibrariesToImport, + @NotNull Set toImport, + @NotNull ModifiableRootModel moduleRootModel, + boolean hasUnresolvedLibraries) { + Set 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, LibraryDependencyData> moduleLibrariesToImport, - @NotNull Map projectLibrariesToImport, - @NotNull Set toImport, - @NotNull ModifiableRootModel moduleRootModel, - boolean hasUnresolvedLibraries) { - Set 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> 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); } } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java index 065c29d8d4ef..ad248d1a9d53 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java @@ -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> toImport, + public void importData(@NotNull Collection> 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> toCreate = filterExistingModules(toImport, project, platformFacade); - if (!toCreate.isEmpty()) { - createModules(toCreate, project, platformFacade); - } - for (DataNode node : toImport) { - Module module = platformFacade.findIdeModule(node.getData(), project); - if (module != null) { - syncPaths(module, platformFacade, node.getData()); - } - } - } - }); + Collection> 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> toCreate, - @NotNull final Project project, - @NotNull final PlatformFacade platformFacade) { - Application application = ApplicationManager.getApplication(); - final Map, Module> moduleMappings = ContainerUtilRt.newHashMap(); - application.runWriteAction(new Runnable() { + @NotNull + private static List createModules(@NotNull Project project, + @NotNull PlatformFacade platformFacade, + Collection> toCreate) { + List models = ContainerUtilRt.newArrayList(); + try { + for (DataNode 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 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 visitor = new RootPolicy() { @Override - public void run() { - for (DataNode module : toCreate) { - importModule(module); - } + public Object visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Object value) { + moduleRootModel.removeOrderEntry(libraryOrderEntry); + return value; } - private void importModule(@NotNull DataNode 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 visitor = new RootPolicy() { - @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> filterExistingModules(@NotNull Collection> modules, - @NotNull Project project, @NotNull PlatformFacade platformFacade) + @NotNull Project project, + @NotNull PlatformFacade platformFacade) { Collection> result = ContainerUtilRt.newArrayList(); for (DataNode node : modules) { @@ -165,30 +154,46 @@ public class ModuleDataService extends AbstractProjectDataService syncModulesPaths(@NotNull Project project, + @NotNull PlatformFacade platformFacade, + Collection> toCreate) { + List models = ContainerUtilRt.newArrayList(); + try { + for (DataNode 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, DataNode> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE); - for (Map.Entry, Collection>> 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 models = ContainerUtilRt.newArrayList(); + try { + MultiMap, DataNode> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE); + for (Map.Entry, Collection>> 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> toImport, - @NotNull final Module module, - @NotNull final PlatformFacade platformFacade, - final boolean synchronous) + @NotNull + private ModifiableRootModel importData(@NotNull Collection> toImport, + @NotNull Module module, + @NotNull PlatformFacade platformFacade) { - ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(module) { - @Override - public void execute() { - ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); - Map , 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, 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 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 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; } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java index 47cf375aa525..033e7d285660 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java @@ -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()); } } }; diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java index 3f724fd3dfb1..e8ab9eddc269 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryImpl.java @@ -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); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryTableBase.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryTableBase.java index 358301a9cb8d..9e32e1f4ffe1 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryTableBase.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/LibraryTableBase.java @@ -167,6 +167,10 @@ public abstract class LibraryTableBase implements PersistentStateComponent addedLibraries = ContainerUtil.newIdentityTroveSet(model.myLibraries); addedLibraries.removeAll(myModel.myLibraries);