Run Kotlin inspection "Variable declaration could be moved inside 'when'" on whole...
[idea/community.git] / plugins / maven / src / main / java / org / jetbrains / idea / maven / importing / MavenProjectImporterImpl.java
1 // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.idea.maven.importing;
3
4 import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration;
5 import com.intellij.openapi.application.ApplicationManager;
6 import com.intellij.openapi.diagnostic.Logger;
7 import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager;
8 import com.intellij.openapi.externalSystem.model.project.ProjectId;
9 import com.intellij.openapi.externalSystem.service.project.*;
10 import com.intellij.openapi.module.Module;
11 import com.intellij.openapi.module.ModuleType;
12 import com.intellij.openapi.module.ModuleWithNameAlreadyExists;
13 import com.intellij.openapi.module.impl.ModulePathKt;
14 import com.intellij.openapi.project.Project;
15 import com.intellij.openapi.roots.LibraryOrderEntry;
16 import com.intellij.openapi.roots.ModifiableRootModel;
17 import com.intellij.openapi.roots.ModuleRootModel;
18 import com.intellij.openapi.roots.OrderEntry;
19 import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
20 import com.intellij.openapi.roots.impl.libraries.LibraryEx;
21 import com.intellij.openapi.roots.libraries.Library;
22 import com.intellij.openapi.ui.Messages;
23 import com.intellij.openapi.util.Pair;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.openapi.vfs.LocalFileSystem;
26 import com.intellij.openapi.vfs.VirtualFile;
27 import com.intellij.util.ArrayUtil;
28 import com.intellij.util.ArrayUtilRt;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.intellij.util.containers.Stack;
31 import com.intellij.workspaceModel.ide.WorkspaceModel;
32 import com.intellij.workspaceModel.ide.legacyBridge.ModuleBridge;
33 import com.intellij.workspaceModel.storage.WorkspaceEntityStorage;
34 import com.intellij.workspaceModel.storage.WorkspaceEntityStorageBuilder;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37 import org.jetbrains.idea.maven.model.MavenId;
38 import org.jetbrains.idea.maven.project.*;
39 import org.jetbrains.idea.maven.utils.MavenLog;
40 import org.jetbrains.idea.maven.utils.MavenUtil;
41 import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions;
42
43 import java.io.IOException;
44 import java.util.*;
45
46 import static org.jetbrains.idea.maven.project.MavenProjectChanges.ALL;
47
48 class MavenProjectImporterImpl extends MavenProjectImporterBase {
49   private static final Logger LOG = Logger.getInstance(MavenProjectImporterImpl.class);
50   private final Project myProject;
51   private final Map<VirtualFile, Module> myFileToModuleMapping;
52   private volatile Set<MavenProject> myAllProjects;
53   private final boolean myImportModuleGroupsRequired;
54
55   private final IdeModifiableModelsProvider myIdeModifiableModelsProvider;
56   private final WorkspaceEntityStorageBuilder myDiff;
57   private ModifiableModelsProviderProxy myModelsProvider;
58   private ModuleModelProxy myModuleModel;
59   private Module myDummyModule;
60
61   private final List<Module> myCreatedModules = new ArrayList<>();
62
63   private final Map<MavenProject, Module> myMavenProjectToModule = new HashMap<>();
64   private final Map<MavenProject, String> myMavenProjectToModuleName = new HashMap<>();
65   private final Map<MavenProject, String> myMavenProjectToModulePath = new HashMap<>();
66
67   MavenProjectImporterImpl(@NotNull Project p,
68                            @NotNull MavenProjectsTree projectsTree,
69                            @NotNull Map<MavenProject, MavenProjectChanges> projectsToImportWithChanges,
70                            boolean importModuleGroupsRequired,
71                            @NotNull IdeModifiableModelsProvider modelsProvider,
72                            @NotNull MavenImportingSettings importingSettings,
73                            @Nullable Module dummyModule) {
74     super(projectsTree, importingSettings, projectsToImportWithChanges);
75     myProject = p;
76     myFileToModuleMapping = getFileToModuleMapping(p, dummyModule, modelsProvider);
77     myImportModuleGroupsRequired = importModuleGroupsRequired;
78     myDummyModule = dummyModule;
79
80     if (modelsProvider instanceof IdeModifiableModelsProviderImpl) {
81       IdeModifiableModelsProviderImpl impl = (IdeModifiableModelsProviderImpl)modelsProvider;
82       myDiff = impl.getActualStorageBuilder();
83     }
84     else {
85       myDiff = null;
86     }
87
88     myIdeModifiableModelsProvider = modelsProvider;
89   }
90
91   @Override
92   @Nullable
93   public List<MavenProjectsProcessorTask> importProject() {
94
95     if (MavenUtil.newModelEnabled(myProject) && myDiff != null) {
96       myModelsProvider = new ModifiableModelsProviderProxyImpl(myProject, myDiff);
97     }
98     else {
99       myModelsProvider = new ModifiableModelsProviderProxyWrapper(myIdeModifiableModelsProvider);
100     }
101     myModuleModel = myModelsProvider.getModuleModelProxy();
102     return importProjectOldWay();
103   }
104
105   @Nullable
106   private List<MavenProjectsProcessorTask> importProjectOldWay() {
107     List<MavenProjectsProcessorTask> postTasks = new ArrayList<>();
108     boolean hasChanges;
109
110     // in the case projects are changed during importing we must memorise them
111     myAllProjects = new LinkedHashSet<>(myProjectsTree.getProjects());
112
113     myAllProjects.addAll(myProjectsToImportWithChanges.keySet()); // some projects may already have been removed from the tree
114
115     hasChanges = deleteIncompatibleModules();
116     myProjectsToImportWithChanges = collectProjectsToImport(myProjectsToImportWithChanges);
117
118     mapMavenProjectsToModulesAndNames();
119
120     if (myProject.isDisposed()) return null;
121
122     final boolean projectsHaveChanges = projectsToImportHaveChanges();
123     final List<MavenModuleImporter> importers = new ArrayList<>();
124     if (projectsHaveChanges) {
125       hasChanges = true;
126       importers.addAll(importModules());
127       scheduleRefreshResolvedArtifacts(postTasks);
128     }
129
130     if (projectsHaveChanges || myImportModuleGroupsRequired) {
131       hasChanges = true;
132       configModuleGroups();
133     }
134
135     if (myProject.isDisposed()) return null;
136
137     List<Module> obsoleteModules = collectObsoleteModules();
138     boolean isDeleteObsoleteModules = isDeleteObsoleteModules(obsoleteModules);
139     hasChanges |= isDeleteObsoleteModules;
140
141     if (hasChanges) {
142       MavenUtil.invokeAndWaitWriteAction(myProject, () -> {
143         ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(() -> {
144           setMavenizedModules(obsoleteModules, false);
145           List<Module> toDelete = new ArrayList<>();
146           if (myDummyModule != null) {
147             toDelete.add(myDummyModule);
148             myDummyModule = null;
149           }
150           if (isDeleteObsoleteModules) {
151             toDelete.addAll(obsoleteModules);
152           }
153           deleteModules(toDelete);
154           removeUnusedProjectLibraries();
155
156           myModelsProvider.commit();
157
158           if (projectsHaveChanges) {
159             removeOutdatedCompilerConfigSettings();
160           }
161
162           if (projectsHaveChanges) {
163             setMavenizedModules(ContainerUtil.map(myProjectsToImportWithChanges.keySet(), myMavenProjectToModule::get), true);
164           }
165         });
166       });
167
168       if (!importers.isEmpty()) {
169         IdeModifiableModelsProvider provider;
170         if (myIdeModifiableModelsProvider instanceof IdeUIModifiableModelsProvider) {
171           provider = myIdeModifiableModelsProvider; // commit does nothing for this provider, so it should be reused
172         }
173         else {
174           provider = ProjectDataManager.getInstance().createModifiableModelsProvider(myProject);
175         }
176
177         try {
178           List<MavenModuleImporter> toRun = new ArrayList<>(importers.size());
179           for (MavenModuleImporter importer : importers) {
180             if (!importer.isModuleDisposed()) {
181               importer.setModifiableModelsProvider(provider);
182               toRun.add(importer);
183             }
184           }
185           configFacets(postTasks, toRun);
186         }
187         finally {
188           MavenUtil.invokeAndWaitWriteAction(myProject, () -> {
189             ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(() -> {
190               provider.commit();
191             });
192           });
193         }
194       }
195
196     }
197     else {
198       MavenUtil.invokeAndWaitWriteAction(myProject, () -> setMavenizedModules(obsoleteModules, false));
199       disposeModifiableModels();
200     }
201
202     return postTasks;
203   }
204
205   private void disposeModifiableModels() {
206     MavenUtil.invokeAndWaitWriteAction(myProject, () -> myModelsProvider.dispose());
207   }
208
209   private Map<MavenProject, MavenProjectChanges> collectProjectsToImport(Map<MavenProject, MavenProjectChanges> projectsToImport) {
210     Map<MavenProject, MavenProjectChanges> result = new HashMap<>(projectsToImport);
211     result.putAll(collectNewlyCreatedProjects()); // e.g. when 'create modules fro aggregators' setting changes
212
213     Set<MavenProject> allProjectsToImport = result.keySet();
214     Set<MavenProject> selectedProjectsToImport = selectProjectsToImport(allProjectsToImport);
215
216     Iterator<MavenProject> it = allProjectsToImport.iterator();
217     while (it.hasNext()) {
218       if (!selectedProjectsToImport.contains(it.next())) it.remove();
219     }
220
221     return result;
222   }
223
224   private Map<MavenProject, MavenProjectChanges> collectNewlyCreatedProjects() {
225     Map<MavenProject, MavenProjectChanges> result = new HashMap<>();
226
227     for (MavenProject each : myAllProjects) {
228       Module module = myFileToModuleMapping.get(each.getFile());
229       if (module == null) {
230         result.put(each, ALL);
231       }
232     }
233
234     return result;
235   }
236
237   private boolean deleteIncompatibleModules() {
238     final Pair<List<Pair<MavenProject, Module>>, List<Pair<MavenProject, Module>>> incompatible = collectIncompatibleModulesWithProjects();
239     final List<Pair<MavenProject, Module>> incompatibleMavenized = incompatible.first;
240     final List<Pair<MavenProject, Module>> incompatibleNotMavenized = incompatible.second;
241
242     if (incompatibleMavenized.isEmpty() && incompatibleNotMavenized.isEmpty()) return false;
243
244     boolean changed = false;
245
246     // For already mavenized modules the type may change because maven project plugins were resolved and MavenImporter asked to create a module of a different type.
247     // In such cases we must change module type silently.
248     for (Pair<MavenProject, Module> each : incompatibleMavenized) {
249       myFileToModuleMapping.remove(each.first.getFile());
250       myModuleModel.disposeModule(each.second);
251       changed = true;
252     }
253
254     if (incompatibleNotMavenized.isEmpty()) return changed;
255
256     final int[] result = new int[]{Messages.OK};
257     if (!ApplicationManager.getApplication().isHeadlessEnvironment()) {
258       MavenUtil.invokeAndWait(myProject, myModelsProvider.getModalityStateForQuestionDialogs(), () -> {
259         String message = MavenProjectBundle.message("maven.import.incompatible.modules",
260                                                     incompatibleNotMavenized.size(),
261                                                     formatProjectsWithModules(incompatibleNotMavenized));
262         String[] options = {
263           MavenProjectBundle.message("maven.import.incompatible.modules.recreate"),
264           MavenProjectBundle.message("maven.import.incompatible.modules.ignore")
265         };
266
267         result[0] = Messages.showOkCancelDialog(myProject, message,
268                                                 MavenProjectBundle.message("maven.project.import.title"),
269                                                 options[0], options[1], Messages.getQuestionIcon());
270       });
271     }
272
273     if (result[0] == Messages.OK) {
274       for (Pair<MavenProject, Module> each : incompatibleNotMavenized) {
275         myFileToModuleMapping.remove(each.first.getFile());
276         myModuleModel.disposeModule(each.second);
277       }
278       changed = true;
279     }
280     else {
281       myProjectsTree.setIgnoredState(MavenUtil.collectFirsts(incompatibleNotMavenized), true, true);
282     }
283
284     return changed;
285   }
286
287   /**
288    * Collects modules that need to change module type
289    *
290    * @return the first List in returned Pair contains already mavenized modules, the second List - not mavenized
291    */
292   private Pair<List<Pair<MavenProject, Module>>, List<Pair<MavenProject, Module>>> collectIncompatibleModulesWithProjects() {
293     List<Pair<MavenProject, Module>> incompatibleMavenized = new ArrayList<>();
294     List<Pair<MavenProject, Module>> incompatibleNotMavenized = new ArrayList<>();
295
296     MavenProjectsManager manager = MavenProjectsManager.getInstance(myProject);
297     for (MavenProject each : myAllProjects) {
298       Module module = myFileToModuleMapping.get(each.getFile());
299       if (module == null) continue;
300
301       if (shouldCreateModuleFor(each) && !(ModuleType.get(module).equals(each.getModuleType()))) {
302         (manager.isMavenizedModule(module) ? incompatibleMavenized : incompatibleNotMavenized).add(Pair.create(each, module));
303       }
304     }
305     return Pair.create(incompatibleMavenized, incompatibleNotMavenized);
306   }
307
308   private static String formatProjectsWithModules(List<Pair<MavenProject, Module>> projectsWithModules) {
309     return StringUtil.join(projectsWithModules, each -> {
310       MavenProject project = each.first;
311       Module module = each.second;
312       return ModuleType.get(module).getName() +
313              " '" +
314              module.getName() +
315              "' for Maven project " +
316              project.getMavenId().getDisplayString();
317     }, "<br>");
318   }
319
320   private void deleteModules(@NotNull List<Module> modules) {
321     for (Module each : modules) {
322       if (!each.isDisposed()) {
323         myModuleModel.disposeModule(each);
324       }
325     }
326   }
327
328   private boolean isDeleteObsoleteModules(@NotNull List<Module> obsoleteModules) {
329     if (obsoleteModules.isEmpty()) {
330       return false;
331     }
332     if (!ApplicationManager.getApplication().isHeadlessEnvironment() || MavenUtil.isMavenUnitTestModeEnabled()) {
333       final int[] result = new int[1];
334       MavenUtil.invokeAndWait(myProject, myModelsProvider.getModalityStateForQuestionDialogs(),
335                               () -> result[0] = Messages.showYesNoDialog(myProject,
336                                                                          MavenProjectBundle.message("maven.import.message.delete.obsolete",
337                                                                                                     formatModules(obsoleteModules)),
338                                                                          MavenProjectBundle.message("maven.project.import.title"),
339                                                                          Messages.getQuestionIcon()));
340
341       if (result[0] == Messages.NO) {
342         return false;
343       }
344     }
345     return true;
346   }
347
348   private List<Module> collectObsoleteModules() {
349     List<Module> remainingModules = new ArrayList<>();
350     Collections.addAll(remainingModules, myModuleModel.getModules());
351
352     for (MavenProject each : selectProjectsToImport(myAllProjects)) {
353       remainingModules.remove(myMavenProjectToModule.get(each));
354     }
355
356     List<Module> obsolete = new ArrayList<>();
357     final MavenProjectsManager manager = MavenProjectsManager.getInstance(myProject);
358     for (Module each : remainingModules) {
359       if (manager.isMavenizedModule(each) && myDummyModule != each) {
360         obsolete.add(each);
361       }
362     }
363     return obsolete;
364   }
365
366   private static String formatModules(final Collection<Module> modules) {
367     StringBuilder res = new StringBuilder();
368
369     int i = 0;
370     for (Module module : modules) {
371       res.append('\'').append(module.getName()).append("'\n");
372
373       if (++i > 20) break;
374     }
375
376     if (i > 20) {
377       res.append("\n ... and other ").append(modules.size() - 20).append(" modules");
378     }
379
380     return res.toString();
381   }
382
383   private void mapMavenProjectsToModulesAndNames() {
384     for (MavenProject each : myAllProjects) {
385       Module module = myFileToModuleMapping.get(each.getFile());
386       if (module != null) {
387         myMavenProjectToModule.put(each, module);
388       }
389     }
390
391     MavenModuleNameMapper.map(myAllProjects,
392                               myMavenProjectToModule,
393                               myMavenProjectToModuleName,
394                               myMavenProjectToModulePath,
395                               myImportingSettings.getDedicatedModuleDir());
396   }
397
398   private void removeOutdatedCompilerConfigSettings() {
399     ApplicationManager.getApplication().assertWriteAccessAllowed();
400
401     final JpsJavaCompilerOptions javacOptions = JavacConfiguration.getOptions(myProject, JavacConfiguration.class);
402     String options = javacOptions.ADDITIONAL_OPTIONS_STRING;
403     options = options.replaceFirst("(-target (\\S+))", ""); // Old IDEAs saved
404     javacOptions.ADDITIONAL_OPTIONS_STRING = options;
405   }
406
407   private List<MavenModuleImporter> importModules() {
408     Map<MavenProject, MavenProjectChanges> projectsWithChanges = myProjectsToImportWithChanges;
409
410     Set<MavenProject> projectsWithNewlyCreatedModules = new HashSet<>();
411
412     for (MavenProject each : projectsWithChanges.keySet()) {
413       if (ensureModuleCreated(each)) {
414         projectsWithNewlyCreatedModules.add(each);
415       }
416     }
417
418     List<MavenModuleImporter> importers = new ArrayList<>();
419
420     for (Map.Entry<MavenProject, MavenProjectChanges> each : projectsWithChanges.entrySet()) {
421       MavenProject project = each.getKey();
422       Module module = myMavenProjectToModule.get(project);
423       boolean isNewModule = projectsWithNewlyCreatedModules.contains(project);
424       MavenId mavenId = project.getMavenId();
425       myModelsProvider.registerModulePublication(
426         module, new ProjectId(mavenId.getGroupId(), mavenId.getArtifactId(), mavenId.getVersion()));
427       MavenModuleImporter moduleImporter = createModuleImporter(module, project, each.getValue());
428       importers.add(moduleImporter);
429
430       MavenRootModelAdapter rootModelAdapter =
431         new MavenRootModelAdapter(new MavenRootModelAdapterLegacyImpl(project, module, myModelsProvider));
432       rootModelAdapter.init(isNewModule);
433       moduleImporter.config(rootModelAdapter);
434     }
435
436     for (MavenProject project : myAllProjects) {
437       if (!projectsWithChanges.containsKey(project)) {
438         Module module = myMavenProjectToModule.get(project);
439         if (module == null) continue;
440
441         importers.add(createModuleImporter(module, project, null));
442       }
443     }
444
445     return importers;
446   }
447
448   private void configFacets(List<MavenProjectsProcessorTask> tasks, List<MavenModuleImporter> importers) {
449     for (MavenModuleImporter importer : importers) {
450       importer.preConfigFacets();
451     }
452
453     for (MavenModuleImporter importer : importers) {
454       importer.configFacets(tasks);
455     }
456
457     for (MavenModuleImporter importer : importers) {
458       importer.postConfigFacets();
459     }
460   }
461
462   private void setMavenizedModules(final Collection<Module> modules, final boolean mavenized) {
463     ApplicationManager.getApplication().assertWriteAccessAllowed();
464     WorkspaceEntityStorage initialStorage = WorkspaceModel.getInstance(myProject).getEntityStorage().getCurrent();
465     WorkspaceEntityStorageBuilder storageBuilder = WorkspaceEntityStorageBuilder.from(initialStorage);
466     for (Module module : modules) {
467       if (module.isDisposed()) continue;
468       ExternalSystemModulePropertyManager modulePropertyManager = ExternalSystemModulePropertyManager.getInstance(module);
469       if (modulePropertyManager instanceof ExternalSystemModulePropertyManagerBridge &&
470           module instanceof ModuleBridge &&
471           ((ModuleBridge)module).getDiff() == null) {
472         ((ExternalSystemModulePropertyManagerBridge)modulePropertyManager).setMavenized(mavenized, storageBuilder);
473       }
474       else {
475         modulePropertyManager.setMavenized(mavenized);
476       }
477     }
478     WorkspaceModel.getInstance(myProject).updateProjectModel(builder -> {
479       builder.addDiff(storageBuilder);
480       return null;
481     });
482   }
483
484   private boolean ensureModuleCreated(MavenProject project) {
485     Module existingModule = myMavenProjectToModule.get(project);
486     if (existingModule != null && existingModule != myDummyModule) return false;
487     final String path = myMavenProjectToModulePath.get(project);
488     String moduleName = ModulePathKt.getModuleNameByFilePath(path);
489     if (isForTheDummyModule(project, existingModule)) {
490       try {
491         if (!myDummyModule.getName().equals(moduleName)) {
492           myModuleModel.renameModule(myDummyModule, moduleName);
493         }
494       }
495       catch (ModuleWithNameAlreadyExists e) {
496         MavenLog.LOG.error("Cannot rename dummy module:", e);
497       }
498       myMavenProjectToModule.put(project, myDummyModule);
499       myCreatedModules.add(myDummyModule);
500       myDummyModule = null;
501       return true;
502     }
503
504
505     // for some reason newModule opens the existing iml file, so we
506     // have to remove it beforehand.
507     deleteExistingImlFile(path);
508     deleteExistingModuleByName(moduleName);
509     final Module module = myModuleModel.newModule(path, project.getModuleType().getId());
510
511     myMavenProjectToModule.put(project, module);
512     myCreatedModules.add(module);
513     return true;
514   }
515
516   private boolean isForTheDummyModule(MavenProject project, Module existingModule) {
517     if (myDummyModule == null) return false;
518     if (existingModule == myDummyModule) return true;
519     return myProjectsTree.getRootProjects().size() == 1 &&
520            myProjectsTree.findRootProject(project) == project;
521   }
522
523   private void deleteExistingModuleByName(final String name) {
524     Module module = myModuleModel.findModuleByName(name);
525     if (module != null) {
526       myModuleModel.disposeModule(module);
527     }
528   }
529
530   private void deleteExistingImlFile(final String path) {
531     MavenUtil.invokeAndWaitWriteAction(myProject, new Runnable() {
532       @Override
533       public void run() {
534         try {
535           VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
536           if (file != null) file.delete(this);
537         }
538         catch (IOException e) {
539           MavenLog.LOG.warn("Cannot delete existing iml file: " + path, e);
540         }
541       }
542     });
543   }
544
545   private MavenModuleImporter createModuleImporter(Module module, MavenProject mavenProject, @Nullable MavenProjectChanges changes) {
546     return new MavenModuleImporter(module,
547                                    myProjectsTree,
548                                    mavenProject,
549                                    changes,
550                                    myMavenProjectToModuleName,
551                                    myImportingSettings,
552                                    myModelsProvider);
553   }
554
555   private void configModuleGroups() {
556     if (!myImportingSettings.isCreateModuleGroups()) return;
557
558     final Stack<String> groups = new Stack<>();
559     final boolean createTopLevelGroup = myProjectsTree.getRootProjects().size() > 1;
560
561     myProjectsTree.visit(new MavenProjectsTree.SimpleVisitor() {
562       int depth = 0;
563
564       @Override
565       public boolean shouldVisit(MavenProject project) {
566         // in case some project has been added while we were importing
567         return myMavenProjectToModuleName.containsKey(project);
568       }
569
570       @Override
571       public void visit(MavenProject each) {
572         depth++;
573
574         String name = myMavenProjectToModuleName.get(each);
575
576         if (shouldCreateGroup(each)) {
577           groups.push(MavenProjectBundle.message("module.group.name", name));
578         }
579
580         if (!shouldCreateModuleFor(each)) {
581           return;
582         }
583
584         Module module = myModuleModel.findModuleByName(name);
585         if (module == null) return;
586         myModuleModel.setModuleGroupPath(module, groups.isEmpty() ? null : ArrayUtilRt.toStringArray(groups));
587       }
588
589       @Override
590       public void leave(MavenProject each) {
591         if (shouldCreateGroup(each)) {
592           groups.pop();
593         }
594         depth--;
595       }
596
597       private boolean shouldCreateGroup(MavenProject project) {
598         return !myProjectsTree.getModules(project).isEmpty()
599                && (createTopLevelGroup || depth > 1);
600       }
601     });
602   }
603
604   private boolean removeUnusedProjectLibraries() {
605     Set<Library> unusedLibraries = new HashSet<>();
606     Collections.addAll(unusedLibraries, myModelsProvider.getAllLibraries());
607
608     for (ModuleRootModel eachModel : collectModuleModels()) {
609       for (OrderEntry eachEntry : eachModel.getOrderEntries()) {
610         if (eachEntry instanceof LibraryOrderEntry) {
611           unusedLibraries.remove(((LibraryOrderEntry)eachEntry).getLibrary());
612         }
613       }
614     }
615
616     boolean removed = false;
617     for (Library each : unusedLibraries) {
618       if (!isDisposed(each) && MavenRootModelAdapter.isMavenLibrary(each) && !MavenRootModelAdapter.isChangedByUser(each)) {
619         myModelsProvider.removeLibrary(each);
620         removed = true;
621       }
622     }
623     return removed;
624   }
625
626   private static boolean isDisposed(Library library) {
627     return library instanceof LibraryEx && ((LibraryEx)library).isDisposed();
628   }
629
630   private Collection<ModuleRootModel> collectModuleModels() {
631     Map<Module, ModuleRootModel> rootModels = new HashMap<>();
632     for (MavenProject each : myProjectsToImportWithChanges.keySet()) {
633       Module module = myMavenProjectToModule.get(each);
634       ModifiableRootModel rootModel = myModelsProvider.getModifiableRootModel(module);
635       rootModels.put(module, rootModel);
636     }
637     for (Module each : myModuleModel.getModules()) {
638       if (rootModels.containsKey(each)) continue;
639       rootModels.put(each, myModelsProvider.getModifiableRootModel(each));
640     }
641     return rootModels.values();
642   }
643
644   @Override
645   public @NotNull List<Module> createdModules() {
646     return myCreatedModules;
647   }
648
649   private static Map<VirtualFile, Module> getFileToModuleMapping(
650     Project project,
651     Module myDummyModule,
652     IdeModifiableModelsProvider modelsProvider) {
653     return MavenProjectsManager.getInstance(project)
654       .getFileToModuleMapping(new MavenModelsProvider() {
655         @Override
656         public Module[] getModules() {
657           return ArrayUtil.remove(modelsProvider.getModules(), myDummyModule);
658         }
659
660         @Override
661         public VirtualFile[] getContentRoots(Module module) {
662           return modelsProvider.getContentRoots(module);
663         }
664       });
665   }
666 }