optimization: fewer getInfoForFile() calls
[idea/community.git] / platform / indexing-impl / src / com / intellij / openapi / module / impl / scopes / ModuleWithDependentsScope.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.module.impl.scopes;
17
18 import com.intellij.openapi.module.Module;
19 import com.intellij.openapi.module.ModuleManager;
20 import com.intellij.openapi.module.UnloadedModuleDescription;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.roots.*;
23 import com.intellij.openapi.roots.impl.DirectoryIndex;
24 import com.intellij.openapi.roots.impl.DirectoryInfo;
25 import com.intellij.openapi.roots.impl.FileIndexBase;
26 import com.intellij.openapi.roots.impl.ProjectFileIndexImpl;
27 import com.intellij.openapi.vfs.VirtualFile;
28 import com.intellij.psi.search.GlobalSearchScope;
29 import com.intellij.psi.search.ProjectScope;
30 import com.intellij.psi.util.CachedValueProvider;
31 import com.intellij.psi.util.CachedValuesManager;
32 import com.intellij.util.containers.ContainerUtil;
33 import com.intellij.util.containers.MultiMap;
34 import com.intellij.util.containers.Queue;
35 import gnu.trove.THashSet;
36 import org.jetbrains.annotations.NonNls;
37 import org.jetbrains.annotations.NotNull;
38
39 import java.util.Collection;
40 import java.util.Set;
41
42 /**
43  * @author max
44  */
45 class ModuleWithDependentsScope extends GlobalSearchScope {
46   private final Module myModule;
47
48   private final ProjectFileIndex myProjectFileIndex;
49   private final Set<Module> myModules;
50   private final GlobalSearchScope myProjectScope;
51
52   ModuleWithDependentsScope(@NotNull Module module) {
53     super(module.getProject());
54     myModule = module;
55
56     myProjectFileIndex = ProjectRootManager.getInstance(module.getProject()).getFileIndex();
57     myProjectScope = ProjectScope.getProjectScope(module.getProject());
58
59     myModules = buildDependents(myModule);
60   }
61
62   @NotNull
63   private static Set<Module> buildDependents(@NotNull Module module) {
64     Set<Module> result = new THashSet<>();
65     result.add(module);
66
67     ModuleIndex index = getModuleIndex(module.getProject());
68
69     Queue<Module> walkingQueue = new Queue<>(10);
70     walkingQueue.addLast(module);
71
72     Set<Module> processedExporting = new THashSet<>();
73     while (!walkingQueue.isEmpty()) {
74       Module current = walkingQueue.pullFirst();
75       processedExporting.add(current);
76       result.addAll(index.plainUsages.get(current));
77       for (Module dependent : index.exportingUsages.get(current)) {
78         result.add(dependent);
79         if (processedExporting.add(dependent)) {
80           walkingQueue.addLast(dependent);
81         }
82       }
83     }
84     return result;
85   }
86
87   private static class ModuleIndex {
88     final MultiMap<Module, Module> plainUsages = MultiMap.create();
89     final MultiMap<Module, Module> exportingUsages = MultiMap.create();
90   }
91
92   @NotNull
93   private static ModuleIndex getModuleIndex(@NotNull Project project) {
94     return CachedValuesManager.getManager(project).getCachedValue(project, () -> {
95       ModuleIndex index = new ModuleIndex();
96       for (Module module : ModuleManager.getInstance(project).getModules()) {
97         for (OrderEntry orderEntry : ModuleRootManager.getInstance(module).getOrderEntries()) {
98           if (orderEntry instanceof ModuleOrderEntry) {
99             Module referenced = ((ModuleOrderEntry)orderEntry).getModule();
100             if (referenced != null) {
101               MultiMap<Module, Module> map = ((ModuleOrderEntry)orderEntry).isExported() ? index.exportingUsages : index.plainUsages;
102               map.putValue(referenced, module);
103             }
104           }
105         }
106       }
107       return CachedValueProvider.Result.create(index, ProjectRootManager.getInstance(project));
108     });
109   }
110
111   @Override
112   public boolean contains(@NotNull VirtualFile file) {
113     return contains(file, false);
114   }
115
116   boolean contains(@NotNull VirtualFile file, boolean myOnlyTests) {
117     if (myProjectFileIndex instanceof FileIndexBase) {
118       // optimization: fewer calls to getInfoForFileOrDirectory()
119       DirectoryInfo info = ((FileIndexBase)myProjectFileIndex).getInfoForFileOrDirectory(file);
120       Module moduleOfFile = info.getModule();
121       if (moduleOfFile == null || !myModules.contains(moduleOfFile)) return false;
122       if (myOnlyTests && !TestSourcesFilter.isTestSources(file, moduleOfFile.getProject())) return false;
123       return ProjectFileIndexImpl.isFileInContent(file, info);
124     }
125     Module moduleOfFile = myProjectFileIndex.getModuleForFile(file);
126     if (moduleOfFile == null || !myModules.contains(moduleOfFile)) return false;
127     if (myOnlyTests && !TestSourcesFilter.isTestSources(file, moduleOfFile.getProject())) return false;
128     return myProjectScope.contains(file);
129   }
130
131   @Override
132   public boolean isSearchInModuleContent(@NotNull Module aModule) {
133     return myModules.contains(aModule);
134   }
135
136   @Override
137   public boolean isSearchInLibraries() {
138     return false;
139   }
140
141   @NotNull
142   @Override
143   public Collection<UnloadedModuleDescription> getUnloadedModulesBelongingToScope() {
144     ModuleManager moduleManager = ModuleManager.getInstance(myModule.getProject());
145     return ContainerUtil.mapNotNull(DirectoryIndex.getInstance(myModule.getProject()).getDependentUnloadedModules(myModule),
146                                     moduleManager::getUnloadedModuleDescription);
147   }
148
149   @Override
150   @NonNls
151   public String toString() {
152     return "Module with dependents:" + myModule.getName();
153   }
154
155   @Override
156   public boolean equals(Object o) {
157     if (this == o) return true;
158     if (!(o instanceof ModuleWithDependentsScope)) return false;
159
160     final ModuleWithDependentsScope moduleWithDependentsScope = (ModuleWithDependentsScope)o;
161
162     return myModule.equals(moduleWithDependentsScope.myModule);
163   }
164
165   @Override
166   public int hashCode() {
167     return myModule.hashCode();
168   }
169 }