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