constructor reference: don't ignore constructor parameters during method reference...
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / quickfix / AddModuleDependencyFix.java
1 // Copyright 2000-2019 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 com.intellij.codeInsight.daemon.impl.quickfix;
3
4 import com.intellij.application.options.ModuleListCellRenderer;
5 import com.intellij.codeInsight.daemon.QuickFixBundle;
6 import com.intellij.codeInsight.daemon.impl.actions.AddImportAction;
7 import com.intellij.openapi.editor.Editor;
8 import com.intellij.openapi.module.Module;
9 import com.intellij.openapi.module.ModuleUtilCore;
10 import com.intellij.openapi.project.DumbService;
11 import com.intellij.openapi.project.Project;
12 import com.intellij.openapi.roots.DependencyScope;
13 import com.intellij.openapi.roots.JavaProjectModelModificationService;
14 import com.intellij.openapi.roots.ModuleRootManager;
15 import com.intellij.openapi.ui.Messages;
16 import com.intellij.openapi.ui.popup.JBPopup;
17 import com.intellij.openapi.ui.popup.JBPopupFactory;
18 import com.intellij.openapi.util.Couple;
19 import com.intellij.psi.*;
20 import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
21 import com.intellij.psi.util.PointersKt;
22 import com.intellij.util.containers.ContainerUtil;
23 import com.intellij.util.modules.CircularModuleDependenciesDetector;
24 import org.jetbrains.annotations.NotNull;
25 import org.jetbrains.annotations.Nullable;
26
27 import java.util.*;
28
29 /**
30  * @author anna
31  */
32 class AddModuleDependencyFix extends OrderEntryFix {
33   private final Module myCurrentModule;
34   private final Set<Module> myModules;
35   private final DependencyScope myScope;
36   private final boolean myExported;
37   private final List<SmartPsiElementPointer<PsiClass>> myClasses;
38
39   AddModuleDependencyFix(PsiReference reference, Module currentModule, DependencyScope scope, List<? extends PsiClass> classes) {
40     super(reference);
41     myCurrentModule = currentModule;
42     myModules = new LinkedHashSet<>();
43     myScope = scope;
44     myExported = false;
45     myClasses = ContainerUtil.map(classes, PointersKt::createSmartPointer);
46
47     PsiElement psiElement = reference.getElement();
48     ModuleRootManager rootManager = ModuleRootManager.getInstance(currentModule);
49     for (PsiClass aClass : classes) {
50       if (isAccessible(aClass, psiElement)) {
51         Module classModule = ModuleUtilCore.findModuleForFile(aClass.getContainingFile());
52         if (classModule != null && classModule != currentModule && !rootManager.isDependsOn(classModule)) {
53           myModules.add(classModule);
54         }
55       }
56     }
57   }
58
59   private static boolean isAccessible(PsiClass aClass, PsiElement refElement) {
60     return JavaResolveUtil.isAccessible(aClass, aClass.getContainingClass(), aClass.getModifierList(), refElement, aClass, null);
61   }
62
63   AddModuleDependencyFix(PsiJavaModuleReference reference, Module currentModule, Set<Module> modules, DependencyScope scope, boolean exported) {
64     super(reference);
65     myCurrentModule = currentModule;
66     myModules = modules;
67     myScope = scope;
68     myExported = exported;
69     myClasses = Collections.emptyList();
70   }
71
72   @Override
73   @NotNull
74   public String getText() {
75     if (myModules.size() == 1) {
76       Module module = ContainerUtil.getFirstItem(myModules);
77       assert module != null;
78       return QuickFixBundle.message("orderEntry.fix.add.dependency.on.module", module.getName());
79     }
80     else {
81       return QuickFixBundle.message("orderEntry.fix.add.dependency.on.module.choose");
82     }
83   }
84
85   @Override
86   @NotNull
87   public String getFamilyName() {
88     return QuickFixBundle.message("orderEntry.fix.family.add.module.dependency");
89   }
90
91   @Override
92   public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
93     return !project.isDisposed() && !myCurrentModule.isDisposed() && !myModules.isEmpty() && myModules.stream().noneMatch(Module::isDisposed);
94   }
95
96   @Override
97   public void invoke(@NotNull Project project, @Nullable Editor editor, PsiFile file) {
98     if (myModules.size() == 1) {
99       addDependencyOnModule(project, editor, ContainerUtil.getFirstItem(myModules));
100     }
101     else {
102       JBPopup popup = JBPopupFactory.getInstance()
103         .createPopupChooserBuilder(new ArrayList<>(myModules))
104         .setRenderer(new ModuleListCellRenderer())
105         .setTitle(QuickFixBundle.message("orderEntry.fix.choose.module.to.add.dependency.on"))
106         .setMovable(false)
107         .setResizable(false)
108         .setRequestFocus(true)
109         .setItemChosenCallback((selectedValue) -> addDependencyOnModule(project, editor, selectedValue))
110         .createPopup();
111       if (editor != null) {
112         popup.showInBestPositionFor(editor);
113       }
114       else {
115         popup.showCenteredInCurrentWindow(project);
116       }
117     }
118   }
119
120   private void addDependencyOnModule(Project project, Editor editor, @Nullable Module module) {
121     if (module == null) return;
122     Couple<Module> circularModules = CircularModuleDependenciesDetector.addingDependencyFormsCircularity(myCurrentModule, module);
123     if (circularModules == null || showCircularWarning(project, circularModules, module)) {
124       JavaProjectModelModificationService.getInstance(project).addDependency(myCurrentModule, module, myScope, myExported);
125
126       if (editor != null && !myClasses.isEmpty()) {
127         PsiClass[] targetClasses = myClasses.stream()
128           .map(SmartPsiElementPointer::getElement)
129           .filter(c -> c != null && ModuleUtilCore.findModuleForPsiElement(c) == module)
130           .toArray(PsiClass[]::new);
131         if (targetClasses.length > 0) {
132           PsiReference ref = restoreReference();
133           if (ref != null) {
134             DumbService.getInstance(project).completeJustSubmittedTasks();
135             new AddImportAction(project, ref, editor, targetClasses).execute();
136           }
137         }
138       }
139     }
140   }
141
142   private static boolean showCircularWarning(Project project, Couple<Module> circle, Module classModule) {
143     String message = QuickFixBundle.message("orderEntry.fix.circular.dependency.warning",
144                                             classModule.getName(), circle.getFirst().getName(), circle.getSecond().getName());
145     String title = QuickFixBundle.message("orderEntry.fix.title.circular.dependency.warning");
146     return Messages.showOkCancelDialog(project, message, title, Messages.getWarningIcon()) == Messages.OK;
147   }
148
149 }