2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.compiler;
18 import com.intellij.compiler.server.BuildManagerListener;
19 import com.intellij.ide.highlighter.JavaFileType;
20 import com.intellij.openapi.fileTypes.FileType;
21 import com.intellij.openapi.module.Module;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.roots.ProjectFileIndex;
24 import com.intellij.openapi.roots.ProjectRootManager;
25 import com.intellij.openapi.util.Key;
26 import com.intellij.openapi.vfs.*;
27 import com.intellij.psi.PsiElement;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.psi.search.GlobalSearchScope;
30 import com.intellij.psi.util.*;
31 import com.intellij.util.containers.ContainerUtil;
32 import com.intellij.util.indexing.FileBasedIndex;
33 import gnu.trove.THashSet;
34 import gnu.trove.TIntHashSet;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37 import org.jetbrains.annotations.TestOnly;
39 import java.util.Collections;
41 import java.util.UUID;
43 import static com.intellij.psi.search.GlobalSearchScope.*;
45 public class CompilerReferenceServiceImpl extends CompilerReferenceService {
46 private static final Key<ParameterizedCachedValue<GlobalSearchScope, CompilerSearchAdapter>> CACHE_KEY = Key.create("compiler.ref.service.search");
47 private final ProjectFileIndex myProjectFileIndex;
48 private final Set<Module> myChangedModules = ContainerUtil.newConcurrentSet();
49 private final Set<FileType> myFileTypes;
51 private volatile CompilerReferenceReader myReader;
52 private volatile GlobalSearchScope myDirtyScope = EMPTY_SCOPE;
54 private final Object myLock = new Object();
56 public CompilerReferenceServiceImpl(Project project) {
58 myProjectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
59 myFileTypes = Collections.unmodifiableSet(ContainerUtil.set(JavaFileType.INSTANCE));
63 public void projectOpened() {
65 myProject.getMessageBus().connect(myProject).subscribe(BuildManagerListener.TOPIC, new BuildManagerListener() {
67 public void beforeBuildProcessStarted(Project project, UUID sessionId) {
71 public void buildStarted(Project project, UUID sessionId, boolean isAutomake) {
76 public void buildFinished(Project project, UUID sessionId, boolean isAutomake) {
77 myChangedModules.clear();
78 myDirtyScope = EMPTY_SCOPE;
83 VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() {
85 public void fileCreated(@NotNull VirtualFileEvent event) {
86 processChange(event.getFile());
90 public void fileCopied(@NotNull VirtualFileCopyEvent event) {
91 processChange(event.getFile());
95 public void fileMoved(@NotNull VirtualFileMoveEvent event) {
96 processChange(event.getFile());
100 public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) {
101 if (VirtualFile.PROP_NAME.equals(event.getPropertyName()) || VirtualFile.PROP_SYMLINK_TARGET.equals(event.getPropertyName())) {
102 processChange(event.getFile());
107 public void beforeContentsChange(@NotNull VirtualFileEvent event) {
108 processChange(event.getFile());
112 public void beforeFileDeletion(@NotNull VirtualFileEvent event) {
113 processChange(event.getFile());
117 public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) {
118 processChange(event.getFile());
121 private void processChange(VirtualFile file) {
122 if (myReader != null && myProjectFileIndex.isInSourceContent(file) && myFileTypes.contains(file.getFileType())) {
123 final Module module = myProjectFileIndex.getModuleForFile(file);
124 if (module != null) {
125 if (myChangedModules.add(module)) {
126 myDirtyScope = myDirtyScope.union(module.getModuleWithDependentsScope());
137 public void projectClosed() {
143 public GlobalSearchScope getScopeWithoutReferences(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) {
144 if (!isServiceEnabled()) return null;
146 final ParameterizedCachedValueProvider<GlobalSearchScope, CompilerSearchAdapter> cachedValueProvider =
147 new ParameterizedCachedValueProvider<GlobalSearchScope, CompilerSearchAdapter>() {
150 public CachedValueProvider.Result<GlobalSearchScope> compute(CompilerSearchAdapter param) {
151 return CachedValueProvider.Result.create(calculateScopeWithoutReferences(element, param), PsiModificationTracker.MODIFICATION_COUNT);
154 return CachedValuesManager.getManager(myProject).getParameterizedCachedValue(element,
161 private boolean isServiceEnabled() {
162 return myReader != null && isEnabled();
166 private GlobalSearchScope calculateScopeWithoutReferences(@NotNull PsiElement element, CompilerSearchAdapter adapter) {
167 TIntHashSet referentFileIds = getReferentFileIds(element, adapter);
168 if (referentFileIds == null) return null;
170 return getScopeRestrictedByFileTypes(new ScopeWithoutReferencesOnCompilation(referentFileIds).intersectWith(notScope(myDirtyScope)),
171 myFileTypes.toArray(new FileType[myFileTypes.size()]));
175 private TIntHashSet getReferentFileIds(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) {
176 final PsiFile file = element.getContainingFile();
177 if (file == null) return null;
178 final VirtualFile vFile = file.getVirtualFile();
179 if (vFile == null) return null;
181 ElementPlace place = ElementPlace.get(vFile, myProjectFileIndex);
186 if (myDirtyScope.contains(vFile)) {
189 CompilerElement[] compilerElements;
190 if (place == ElementPlace.SRC) {
191 final CompilerElement compilerElement = adapter.asCompilerElement(element);
192 compilerElements = compilerElement == null ? CompilerElement.EMPTY_ARRAY : new CompilerElement[]{compilerElement};
195 compilerElements = adapter.libraryElementAsCompilerElements(element);
197 if (compilerElements.length == 0) return null;
199 synchronized (myLock) {
200 if (myReader == null) return null;
201 TIntHashSet referentFileIds = new TIntHashSet();
202 for (CompilerElement compilerElement : compilerElements) {
203 referentFileIds.addAll(myReader.findReferentFileIds(compilerElement, adapter).toArray());
205 return referentFileIds;
209 private void closeReaderIfNeed() {
210 synchronized (myLock) {
211 if (myReader != null) {
218 private void openReaderIfNeed() {
219 synchronized (myLock) {
220 if (myProject.isOpen()) {
221 myReader = CompilerReferenceReader.create(myProject);
228 public Set<VirtualFile> getReferentFiles(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) {
229 FileBasedIndex fileIndex = FileBasedIndex.getInstance();
230 final TIntHashSet ids = getReferentFileIds(element, adapter);
231 if (ids == null) return null;
232 Set<VirtualFile> fileSet = new THashSet<>();
234 final VirtualFile vFile = fileIndex.findFileById(myProject, id);
235 assert vFile != null;
242 private enum ElementPlace {
245 private static ElementPlace get(VirtualFile file, ProjectFileIndex index) {
246 return index.isInSourceContent(file) ? SRC :
247 ((index.isInLibrarySource(file) || index.isInLibraryClasses(file)) ? LIB : null);
251 private static class ScopeWithoutReferencesOnCompilation extends GlobalSearchScope {
252 private final TIntHashSet myReferentIds;
254 private ScopeWithoutReferencesOnCompilation(TIntHashSet ids) {
259 public boolean contains(@NotNull VirtualFile file) {
260 return !(file instanceof VirtualFileWithId) || !myReferentIds.contains(((VirtualFileWithId)file).getId());
264 public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
269 public boolean isSearchInModuleContent(@NotNull Module aModule) {
274 public boolean isSearchInLibraries() {