e19c4109a0b8182527c8caf87b15230a094f8e29
[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.vfs.VirtualFile;
25 import com.intellij.psi.search.GlobalSearchScope;
26 import com.intellij.psi.search.ProjectScope;
27 import com.intellij.psi.util.CachedValueProvider;
28 import com.intellij.psi.util.CachedValuesManager;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.intellij.util.containers.MultiMap;
31 import com.intellij.util.containers.Queue;
32 import gnu.trove.THashSet;
33 import org.jetbrains.annotations.NonNls;
34 import org.jetbrains.annotations.NotNull;
35
36 import java.util.Collection;
37 import java.util.Set;
38
39 /**
40  * @author max
41  */
42 class ModuleWithDependentsScope extends GlobalSearchScope {
43   private final Module myModule;
44
45   private final ProjectFileIndex myProjectFileIndex;
46   private final Set<Module> myModules;
47   private final GlobalSearchScope myProjectScope;
48
49   ModuleWithDependentsScope(@NotNull Module module) {
50     super(module.getProject());
51     myModule = module;
52
53     myProjectFileIndex = ProjectRootManager.getInstance(module.getProject()).getFileIndex();
54     myProjectScope = ProjectScope.getProjectScope(module.getProject());
55
56     myModules = buildDependents(myModule);
57   }
58
59   @NotNull
60   private static Set<Module> buildDependents(Module module) {
61     Set<Module> result = new THashSet<>();
62     result.add(module);
63
64     ModuleIndex index = getModuleIndex(module.getProject());
65
66     Queue<Module> walkingQueue = new Queue<>(10);
67     walkingQueue.addLast(module);
68
69     Set<Module> processedExporting = new THashSet<>();
70     while (!walkingQueue.isEmpty()) {
71       Module current = walkingQueue.pullFirst();
72       processedExporting.add(current);
73       result.addAll(index.plainUsages.get(current));
74       for (Module dependent : index.exportingUsages.get(current)) {
75         result.add(dependent);
76         if (processedExporting.add(dependent)) {
77           walkingQueue.addLast(dependent);
78         }
79       }
80     }
81     return result;
82   }
83
84   private static class ModuleIndex {
85     final MultiMap<Module, Module> plainUsages = MultiMap.create();
86     final MultiMap<Module, Module> exportingUsages = MultiMap.create();
87   }
88
89   private static ModuleIndex getModuleIndex(final Project project) {
90     return CachedValuesManager.getManager(project).getCachedValue(project, () -> {
91       ModuleIndex index = new ModuleIndex();
92       for (Module module : ModuleManager.getInstance(project).getModules()) {
93         for (OrderEntry orderEntry : ModuleRootManager.getInstance(module).getOrderEntries()) {
94           if (orderEntry instanceof ModuleOrderEntry) {
95             Module referenced = ((ModuleOrderEntry)orderEntry).getModule();
96             if (referenced != null) {
97               MultiMap<Module, Module> map = ((ModuleOrderEntry)orderEntry).isExported() ? index.exportingUsages : index.plainUsages;
98               map.putValue(referenced, module);
99             }
100           }
101         }
102       }
103       return CachedValueProvider.Result.create(index, ProjectRootManager.getInstance(project));
104     });
105   }
106
107   @Override
108   public boolean contains(@NotNull VirtualFile file) {
109     return contains(file, false);
110   }
111
112   boolean contains(@NotNull VirtualFile file, boolean myOnlyTests) {
113     Module moduleOfFile = myProjectFileIndex.getModuleForFile(file);
114     if (moduleOfFile == null || !myModules.contains(moduleOfFile)) return false;
115     if (myOnlyTests && !TestSourcesFilter.isTestSources(file, moduleOfFile.getProject())) return false;
116     return myProjectScope.contains(file);
117   }
118
119   @Override
120   public boolean isSearchInModuleContent(@NotNull Module aModule) {
121     return myModules.contains(aModule);
122   }
123
124   @Override
125   public boolean isSearchInLibraries() {
126     return false;
127   }
128
129   @NotNull
130   @Override
131   public Collection<UnloadedModuleDescription> getUnloadedModulesBelongingToScope() {
132     ModuleManager moduleManager = ModuleManager.getInstance(myModule.getProject());
133     return ContainerUtil.mapNotNull(DirectoryIndex.getInstance(myModule.getProject()).getDependentUnloadedModules(myModule),
134                                     moduleManager::getUnloadedModuleDescription);
135   }
136
137   @Override
138   @NonNls
139   public String toString() {
140     return "Module with dependents:" + myModule.getName();
141   }
142
143   @Override
144   public boolean equals(Object o) {
145     if (this == o) return true;
146     if (!(o instanceof ModuleWithDependentsScope)) return false;
147
148     final ModuleWithDependentsScope moduleWithDependentsScope = (ModuleWithDependentsScope)o;
149
150     return myModule.equals(moduleWithDependentsScope.myModule);
151   }
152
153   @Override
154   public int hashCode() {
155     return myModule.hashCode();
156   }
157 }