fix "IDEA-221944 Deadlock on opening second project" and support preloading for proje...
[idea/community.git] / platform / projectModel-impl / src / com / intellij / openapi / roots / impl / ModuleFileIndexImpl.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.openapi.roots.impl;
3
4 import com.intellij.openapi.application.ReadAction;
5 import com.intellij.openapi.module.Module;
6 import com.intellij.openapi.roots.*;
7 import com.intellij.openapi.vfs.VirtualFile;
8 import com.intellij.openapi.vfs.VirtualFileFilter;
9 import com.intellij.util.IncorrectOperationException;
10 import org.jetbrains.annotations.NotNull;
11 import org.jetbrains.annotations.Nullable;
12 import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
13
14 import java.util.*;
15
16 public final class ModuleFileIndexImpl extends FileIndexBase implements ModuleFileIndex {
17   @NotNull
18   private final Module myModule;
19
20   public ModuleFileIndexImpl(@NotNull Module module) {
21     super(DirectoryIndex.getInstance(module.getProject()));
22
23     myModule = module;
24   }
25
26   @Override
27   public boolean iterateContent(@NotNull ContentIterator processor, @Nullable VirtualFileFilter filter) {
28     Set<VirtualFile> contentRoots = getModuleRootsToIterate();
29     for (VirtualFile contentRoot : contentRoots) {
30       if (!iterateContentUnderDirectory(contentRoot, processor, filter)) {
31         return false;
32       }
33     }
34     return true;
35   }
36
37
38   @NotNull
39   Set<VirtualFile> getModuleRootsToIterate() {
40     return ReadAction.compute(() -> {
41       if (myModule.isDisposed()) return Collections.emptySet();
42       Set<VirtualFile> result = new LinkedHashSet<>();
43       VirtualFile[][] allRoots = getModuleContentAndSourceRoots(myModule);
44       for (VirtualFile[] roots : allRoots) {
45         for (VirtualFile root : roots) {
46           DirectoryInfo info = getInfoForFileOrDirectory(root);
47           if (!info.isInProject(root)) continue;
48
49           VirtualFile parent = root.getParent();
50           if (parent != null) {
51             DirectoryInfo parentInfo = myDirectoryIndex.getInfoForFile(parent);
52             if (myModule.equals(parentInfo.getModule())) continue; // inner content - skip it
53           }
54           result.add(root);
55         }
56       }
57       return result;
58     });
59   }
60
61   @Override
62   public boolean isInContent(@NotNull VirtualFile fileOrDir) {
63     return isInContent(fileOrDir, getInfoForFileOrDirectory(fileOrDir));
64   }
65
66   @Override
67   public boolean isInSourceContent(@NotNull VirtualFile fileOrDir) {
68     DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir);
69     return info.isInModuleSource(fileOrDir) && myModule.equals(info.getModule());
70   }
71
72   @Override
73   @NotNull
74   public List<OrderEntry> getOrderEntriesForFile(@NotNull VirtualFile fileOrDir) {
75     return findAllOrderEntriesWithOwnerModule(myModule, myDirectoryIndex.getOrderEntries(getInfoForFileOrDirectory(fileOrDir)));
76   }
77
78   @Override
79   public OrderEntry getOrderEntryForFile(@NotNull VirtualFile fileOrDir) {
80     return findOrderEntryWithOwnerModule(myModule, myDirectoryIndex.getOrderEntries(getInfoForFileOrDirectory(fileOrDir)));
81   }
82
83   @Override
84   public boolean isInTestSourceContent(@NotNull VirtualFile fileOrDir) {
85     DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir);
86     return info.isInModuleSource(fileOrDir) && myModule.equals(info.getModule()) && isTestSourcesRoot(info);
87   }
88
89   @Override
90   public boolean isUnderSourceRootOfType(@NotNull VirtualFile fileOrDir, @NotNull Set<? extends JpsModuleSourceRootType<?>> rootTypes) {
91     DirectoryInfo info = getInfoForFileOrDirectory(fileOrDir);
92     return info.isInModuleSource(fileOrDir) && myModule.equals(info.getModule()) && rootTypes.contains(myDirectoryIndex.getSourceRootType(info));
93   }
94
95   @Override
96   protected boolean isScopeDisposed() {
97     return myModule.isDisposed();
98   }
99
100   @Nullable
101   public static OrderEntry findOrderEntryWithOwnerModule(@NotNull Module ownerModule, @NotNull List<? extends OrderEntry> orderEntries) {
102     if (orderEntries.size() < 10) {
103       for (OrderEntry orderEntry : orderEntries) {
104         if (orderEntry.getOwnerModule() == ownerModule) {
105           return orderEntry;
106         }
107       }
108       return null;
109     }
110     int index = Collections.binarySearch(orderEntries, new FakeOrderEntry(ownerModule), RootIndex.BY_OWNER_MODULE);
111     return index < 0 ? null : orderEntries.get(index);
112   }
113
114   @NotNull
115   private static List<OrderEntry> findAllOrderEntriesWithOwnerModule(@NotNull Module ownerModule, @NotNull List<? extends OrderEntry> entries) {
116     if (entries.isEmpty()) return Collections.emptyList();
117
118     if (entries.size() == 1) {
119       OrderEntry entry = entries.get(0);
120       return entry.getOwnerModule() == ownerModule ?
121              new ArrayList<>(entries) : Collections.emptyList();
122     }
123     int index = Collections.binarySearch(entries, new FakeOrderEntry(ownerModule), RootIndex.BY_OWNER_MODULE);
124     if (index < 0) {
125       return Collections.emptyList();
126     }
127     int firstIndex = index;
128     while (firstIndex - 1 >= 0 && entries.get(firstIndex - 1).getOwnerModule() == ownerModule) {
129       firstIndex--;
130     }
131     int lastIndex = index + 1;
132     while (lastIndex < entries.size() && entries.get(lastIndex).getOwnerModule() == ownerModule) {
133       lastIndex++;
134     }
135     return new ArrayList<>(entries.subList(firstIndex, lastIndex));
136   }
137
138   private static class FakeOrderEntry implements OrderEntry {
139     private final Module myOwnerModule;
140
141     FakeOrderEntry(@NotNull Module ownerModule) {
142       myOwnerModule = ownerModule;
143     }
144
145     @NotNull
146     @Override
147     public VirtualFile[] getFiles(@NotNull OrderRootType type) {
148       throw new IncorrectOperationException();
149     }
150
151     @NotNull
152     @Override
153     public String[] getUrls(@NotNull OrderRootType rootType) {
154       throw new IncorrectOperationException();
155     }
156
157     @NotNull
158     @Override
159     public String getPresentableName() {
160       throw new IncorrectOperationException();
161     }
162
163     @Override
164     public boolean isValid() {
165       throw new IncorrectOperationException();
166     }
167
168     @NotNull
169     @Override
170     public Module getOwnerModule() {
171       return myOwnerModule;
172     }
173
174     @Override
175     public <R> R accept(@NotNull RootPolicy<R> policy, @Nullable R initialValue) {
176       throw new IncorrectOperationException();
177     }
178
179     @Override
180     public int compareTo(@NotNull OrderEntry o) {
181       throw new IncorrectOperationException();
182     }
183
184     @Override
185     public boolean isSynthetic() {
186       throw new IncorrectOperationException();
187     }
188   }
189
190   @Override
191   protected boolean isInContent(@NotNull VirtualFile file, @NotNull DirectoryInfo info) {
192     return ProjectFileIndexImpl.isFileInContent(file, info) && myModule.equals(info.getModule());
193   }
194 }