IG: use ThreeState enum instead of inventing own
[idea/community.git] / platform / projectModel-api / src / com / intellij / openapi / module / ModuleUtilCore.java
1 // Copyright 2000-2020 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.openapi.module;
3
4 import com.intellij.openapi.application.ReadAction;
5 import com.intellij.openapi.project.Project;
6 import com.intellij.openapi.roots.*;
7 import com.intellij.openapi.util.Key;
8 import com.intellij.openapi.util.text.StringUtil;
9 import com.intellij.openapi.vfs.VirtualFile;
10 import com.intellij.psi.PsiElement;
11 import com.intellij.psi.PsiFile;
12 import com.intellij.psi.PsiFileSystemItem;
13 import com.intellij.util.PathUtilRt;
14 import com.intellij.util.graph.Graph;
15 import org.jetbrains.annotations.NotNull;
16 import org.jetbrains.annotations.Nullable;
17
18 import java.util.*;
19
20 public class ModuleUtilCore {
21   public static final Key<Module> KEY_MODULE = new Key<>("Module");
22
23   public static boolean projectContainsFile(@NotNull Project project, @NotNull VirtualFile file, boolean isLibraryElement) {
24     ProjectFileIndex projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
25     if (isLibraryElement) {
26       List<OrderEntry> orders = projectFileIndex.getOrderEntriesForFile(file);
27       for(OrderEntry orderEntry:orders) {
28         if (orderEntry instanceof JdkOrderEntry || orderEntry instanceof LibraryOrderEntry) {
29           return true;
30         }
31       }
32       return false;
33     }
34     else {
35       return projectFileIndex.isInContent(file);
36     }
37   }
38
39   @NotNull
40   public static String getModuleNameInReadAction(@NotNull final Module module) {
41     return ReadAction.compute(module::getName);
42   }
43
44   public static boolean isModuleDisposed(@NotNull PsiElement element) {
45     if (!element.isValid()) return true;
46     final Project project = element.getProject();
47     ProjectFileIndex projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
48     final PsiFile file = element.getContainingFile();
49     if (file == null) return true;
50     VirtualFile vFile = file.getVirtualFile();
51     final Module module = vFile == null ? null : projectFileIndex.getModuleForFile(vFile);
52     // element may be in library
53     return module == null ? !projectFileIndex.isInLibraryClasses(vFile) : module.isDisposed();
54   }
55
56   @Nullable
57   public static Module findModuleForFile(@Nullable PsiFile containingFile) {
58     if (containingFile != null) {
59       VirtualFile vFile = containingFile.getVirtualFile();
60       if (vFile != null) {
61         return findModuleForFile(vFile, containingFile.getProject());
62       }
63     }
64     return null;
65   }
66
67   @Nullable
68   public static Module findModuleForFile(@NotNull VirtualFile file, @NotNull Project project) {
69     if (project.isDefault()) {
70       return null;
71     }
72     return ProjectFileIndex.getInstance(project).getModuleForFile(file);
73   }
74
75   @Nullable
76   public static Module findModuleForPsiElement(@NotNull PsiElement element) {
77     PsiFile containingFile = element.getContainingFile();
78     if (containingFile == null) {
79       if (!element.isValid()) return null;
80     }
81     else {
82       if (!containingFile.isValid()) return null;
83     }
84
85     Project project = (containingFile == null ? element : containingFile).getProject();
86     if (project.isDefault()) return null;
87     final ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(project);
88
89     if (element instanceof PsiFileSystemItem && (!(element instanceof PsiFile) || element.getContext() == null)) {
90       VirtualFile vFile = ((PsiFileSystemItem)element).getVirtualFile();
91       if (vFile == null) {
92         vFile = containingFile == null ? null : containingFile.getOriginalFile().getVirtualFile();
93         if (vFile == null) {
94           return element.getUserData(KEY_MODULE);
95         }
96       }
97       if (fileIndex.isInLibrary(vFile)) {
98         final List<OrderEntry> orderEntries = fileIndex.getOrderEntriesForFile(vFile);
99         if (orderEntries.isEmpty()) {
100           return null;
101         }
102         if (orderEntries.size() == 1) {
103           return orderEntries.get(0).getOwnerModule();
104         }
105         Set<Module> modules = new HashSet<>();
106         for (OrderEntry orderEntry : orderEntries) {
107           modules.add(orderEntry.getOwnerModule());
108         }
109         final Module[] candidates = modules.toArray(Module.EMPTY_ARRAY);
110         Arrays.sort(candidates, ModuleManager.getInstance(project).moduleDependencyComparator());
111         return candidates[0];
112       }
113       return fileIndex.getModuleForFile(vFile);
114     }
115     if (containingFile != null) {
116       PsiElement context;
117       while ((context = containingFile.getContext()) != null) {
118         final PsiFile file = context.getContainingFile();
119         if (file == null) break;
120         containingFile = file;
121       }
122
123       if (containingFile.getUserData(KEY_MODULE) != null) {
124         return containingFile.getUserData(KEY_MODULE);
125       }
126
127       final PsiFile originalFile = containingFile.getOriginalFile();
128       if (originalFile.getUserData(KEY_MODULE) != null) {
129         return originalFile.getUserData(KEY_MODULE);
130       }
131
132       final VirtualFile virtualFile = originalFile.getVirtualFile();
133       if (virtualFile != null) {
134         return fileIndex.getModuleForFile(virtualFile);
135       }
136     }
137
138     return element.getUserData(KEY_MODULE);
139   }
140
141   //ignores export flag
142   public static void getDependencies(@NotNull Module module, @NotNull Set<? super Module> modules) {
143     if (modules.contains(module)) return;
144     modules.add(module);
145     Module[] dependencies = ModuleRootManager.getInstance(module).getDependencies();
146     for (Module dependency : dependencies) {
147       getDependencies(dependency, modules);
148     }
149   }
150
151   /**
152    * collect transitive module dependants
153    * @param module to find dependencies on
154    * @param result resulted set
155    */
156   public static void collectModulesDependsOn(@NotNull final Module module, @NotNull Set<? super Module> result) {
157     if (!result.add(module)) {
158       return;
159     }
160
161     final ModuleManager moduleManager = ModuleManager.getInstance(module.getProject());
162     final List<Module> dependentModules = moduleManager.getModuleDependentModules(module);
163     for (final Module dependentModule : dependentModules) {
164       final OrderEntry[] orderEntries = ModuleRootManager.getInstance(dependentModule).getOrderEntries();
165       for (OrderEntry o : orderEntries) {
166         if (o instanceof ModuleOrderEntry) {
167           final ModuleOrderEntry orderEntry = (ModuleOrderEntry)o;
168           if (orderEntry.getModule() == module) {
169             if (orderEntry.isExported()) {
170               collectModulesDependsOn(dependentModule, result);
171             }
172             else {
173               result.add(dependentModule);
174             }
175             break;
176           }
177         }
178       }
179     }
180   }
181
182   @NotNull
183   public static List<Module> getAllDependentModules(@NotNull Module module) {
184     List<Module> list = new ArrayList<>();
185     Graph<Module> graph = ModuleManager.getInstance(module.getProject()).moduleGraph();
186     for (Iterator<Module> i = graph.getOut(module); i.hasNext();) {
187       list.add(i.next());
188     }
189     return list;
190   }
191
192   public static boolean visitMeAndDependentModules(@NotNull final Module module, @NotNull ModuleVisitor visitor) {
193     if (!visitor.visit(module)) {
194       return false;
195     }
196     final List<Module> list = getAllDependentModules(module);
197     for (Module dependentModule : list) {
198       if (!visitor.visit(dependentModule)) {
199         return false;
200       }
201     }
202     return true;
203   }
204
205   public static boolean moduleContainsFile(@NotNull final Module module, @NotNull VirtualFile file, boolean isLibraryElement) {
206     ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
207     if (isLibraryElement) {
208       OrderEntry orderEntry = moduleRootManager.getFileIndex().getOrderEntryForFile(file);
209       return orderEntry instanceof JdkOrderEntry || orderEntry instanceof LibraryOrderEntry;
210     }
211     else {
212       return moduleRootManager.getFileIndex().isInContent(file);
213     }
214   }
215
216   public static boolean isModuleFile(@NotNull Module module, @NotNull VirtualFile file) {
217     return StringUtil.equal(file.getPath(), module.getModuleFilePath(), file.isCaseSensitive());
218   }
219
220   public static boolean isModuleDir(@NotNull Module module, @NotNull VirtualFile dir) {
221     return StringUtil.equal(dir.getPath(), getModuleDirPath(module), dir.isCaseSensitive());
222   }
223
224   @NotNull
225   public static String getModuleDirPath(@NotNull Module module) {
226     return PathUtilRt.getParentPath(module.getModuleFilePath());
227   }
228
229   @FunctionalInterface
230   public interface ModuleVisitor {
231     /**
232      * @param module module to be visited.
233      * @return false to stop visiting.
234      */
235     boolean visit(@NotNull Module module);
236   }
237 }