89a749ac5ca52ac3949274c288aef7baf3fe671a
[idea/community.git] / platform / projectModel-api / src / com / intellij / openapi / module / ModuleUtilCore.java
1 // Copyright 2000-2017 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.io.FileUtil;
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     return ProjectFileIndex.SERVICE.getInstance(project).getModuleForFile(file);
70   }
71
72   @Nullable
73   public static Module findModuleForPsiElement(@NotNull PsiElement element) {
74     PsiFile containingFile = element.getContainingFile();
75     if (containingFile == null) {
76       if (!element.isValid()) return null;
77     }
78     else {
79       if (!containingFile.isValid()) return null;
80     }
81
82     Project project = (containingFile == null ? element : containingFile).getProject();
83     if (project.isDefault()) return null;
84     final ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(project);
85
86     if (element instanceof PsiFileSystemItem && (!(element instanceof PsiFile) || element.getContext() == null)) {
87       VirtualFile vFile = ((PsiFileSystemItem)element).getVirtualFile();
88       if (vFile == null) {
89         vFile = containingFile == null ? null : containingFile.getOriginalFile().getVirtualFile();
90         if (vFile == null) {
91           return element.getUserData(KEY_MODULE);
92         }
93       }
94       if (fileIndex.isInLibrary(vFile)) {
95         final List<OrderEntry> orderEntries = fileIndex.getOrderEntriesForFile(vFile);
96         if (orderEntries.isEmpty()) {
97           return null;
98         }
99         if (orderEntries.size() == 1) {
100           return orderEntries.get(0).getOwnerModule();
101         }
102         Set<Module> modules = new HashSet<>();
103         for (OrderEntry orderEntry : orderEntries) {
104           modules.add(orderEntry.getOwnerModule());
105         }
106         final Module[] candidates = modules.toArray(Module.EMPTY_ARRAY);
107         Arrays.sort(candidates, ModuleManager.getInstance(project).moduleDependencyComparator());
108         return candidates[0];
109       }
110       return fileIndex.getModuleForFile(vFile);
111     }
112     if (containingFile != null) {
113       PsiElement context;
114       while ((context = containingFile.getContext()) != null) {
115         final PsiFile file = context.getContainingFile();
116         if (file == null) break;
117         containingFile = file;
118       }
119
120       if (containingFile.getUserData(KEY_MODULE) != null) {
121         return containingFile.getUserData(KEY_MODULE);
122       }
123
124       final PsiFile originalFile = containingFile.getOriginalFile();
125       if (originalFile.getUserData(KEY_MODULE) != null) {
126         return originalFile.getUserData(KEY_MODULE);
127       }
128
129       final VirtualFile virtualFile = originalFile.getVirtualFile();
130       if (virtualFile != null) {
131         return fileIndex.getModuleForFile(virtualFile);
132       }
133     }
134
135     return element.getUserData(KEY_MODULE);
136   }
137
138   //ignores export flag
139   public static void getDependencies(@NotNull Module module, @NotNull Set<? super Module> modules) {
140     if (modules.contains(module)) return;
141     modules.add(module);
142     Module[] dependencies = ModuleRootManager.getInstance(module).getDependencies();
143     for (Module dependency : dependencies) {
144       getDependencies(dependency, modules);
145     }
146   }
147
148   /**
149    * collect transitive module dependants
150    * @param module to find dependencies on
151    * @param result resulted set
152    */
153   public static void collectModulesDependsOn(@NotNull final Module module, @NotNull Set<? super Module> result) {
154     if (!result.add(module)) {
155       return;
156     }
157
158     final ModuleManager moduleManager = ModuleManager.getInstance(module.getProject());
159     final List<Module> dependentModules = moduleManager.getModuleDependentModules(module);
160     for (final Module dependentModule : dependentModules) {
161       final OrderEntry[] orderEntries = ModuleRootManager.getInstance(dependentModule).getOrderEntries();
162       for (OrderEntry o : orderEntries) {
163         if (o instanceof ModuleOrderEntry) {
164           final ModuleOrderEntry orderEntry = (ModuleOrderEntry)o;
165           if (orderEntry.getModule() == module) {
166             if (orderEntry.isExported()) {
167               collectModulesDependsOn(dependentModule, result);
168             }
169             else {
170               result.add(dependentModule);
171             }
172             break;
173           }
174         }
175       }
176     }
177   }
178
179   @NotNull
180   public static List<Module> getAllDependentModules(@NotNull Module module) {
181     final ArrayList<Module> list = new ArrayList<>();
182     final Graph<Module> graph = ModuleManager.getInstance(module.getProject()).moduleGraph();
183     for (Iterator<Module> i = graph.getOut(module); i.hasNext();) {
184       list.add(i.next());
185     }
186     return list;
187   }
188
189   public static boolean visitMeAndDependentModules(@NotNull final Module module, @NotNull ModuleVisitor visitor) {
190     if (!visitor.visit(module)) {
191       return false;
192     }
193     final List<Module> list = getAllDependentModules(module);
194     for (Module dependentModule : list) {
195       if (!visitor.visit(dependentModule)) {
196         return false;
197       }
198     }
199     return true;
200   }
201
202   public static boolean moduleContainsFile(@NotNull final Module module, @NotNull VirtualFile file, boolean isLibraryElement) {
203     ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
204     if (isLibraryElement) {
205       OrderEntry orderEntry = moduleRootManager.getFileIndex().getOrderEntryForFile(file);
206       return orderEntry instanceof JdkOrderEntry || orderEntry instanceof LibraryOrderEntry;
207     }
208     else {
209       return moduleRootManager.getFileIndex().isInContent(file);
210     }
211   }
212
213   public static boolean isModuleFile(@NotNull Module module, @NotNull VirtualFile file) {
214     return FileUtil.namesEqual(file.getPath(), module.getModuleFilePath());
215   }
216
217   public static boolean isModuleDir(@NotNull Module module, @NotNull VirtualFile dir) {
218     return FileUtil.namesEqual(dir.getPath(), getModuleDirPath(module));
219   }
220
221   @NotNull
222   public static String getModuleDirPath(@NotNull Module module) {
223     return PathUtilRt.getParentPath(module.getModuleFilePath());
224   }
225
226   @FunctionalInterface
227   public interface ModuleVisitor {
228     /**
229      * @param module module to be visited.
230      * @return false to stop visiting.
231      */
232     boolean visit(@NotNull Module module);
233   }
234 }