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.ide.highlighter.JavaFileType;
19 import com.intellij.openapi.compiler.CompileContext;
20 import com.intellij.openapi.compiler.CompileTask;
21 import com.intellij.openapi.compiler.CompilerManager;
22 import com.intellij.openapi.fileTypes.FileType;
23 import com.intellij.openapi.module.Module;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.roots.ProjectFileIndex;
26 import com.intellij.openapi.roots.ProjectRootManager;
27 import com.intellij.openapi.util.Key;
28 import com.intellij.openapi.vfs.*;
29 import com.intellij.psi.PsiElement;
30 import com.intellij.psi.PsiFile;
31 import com.intellij.psi.search.GlobalSearchScope;
32 import com.intellij.psi.util.*;
33 import com.intellij.util.containers.ContainerUtil;
34 import com.intellij.util.indexing.FileBasedIndex;
35 import gnu.trove.THashSet;
36 import gnu.trove.TIntHashSet;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39 import org.jetbrains.annotations.TestOnly;
41 import java.util.Collections;
44 import static com.intellij.psi.search.GlobalSearchScope.*;
45 import static com.intellij.psi.search.GlobalSearchScope.notScope;
47 public class CompilerReferenceServiceImpl extends CompilerReferenceService {
48 private static final Key<ParameterizedCachedValue<GlobalSearchScope, CompilerSearchAdapter>> CACHE_KEY = Key.create("compiler.ref.service.search");
49 private final ProjectFileIndex myProjectFileIndex;
50 private final Set<Module> myChangedModules = ContainerUtil.newConcurrentSet();
51 private final Set<FileType> myFileTypes;
53 private volatile CompilerReferenceReader myReader;
54 private volatile GlobalSearchScope myDirtyScope = EMPTY_SCOPE;
56 private final Object myLock = new Object();
58 public CompilerReferenceServiceImpl(Project project) {
60 myProjectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
61 myFileTypes = Collections.unmodifiableSet(ContainerUtil.set(JavaFileType.INSTANCE));
65 public void projectOpened() {
67 CompilerManager.getInstance(myProject).addBeforeTask(new CompileTask() {
69 public boolean execute(CompileContext context) {
75 CompilerManager.getInstance(myProject).addAfterTask(new CompileTask() {
77 public boolean execute(CompileContext context) {
78 myChangedModules.clear();
79 myDirtyScope = EMPTY_SCOPE;
85 VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() {
87 public void fileCreated(@NotNull VirtualFileEvent event) {
88 processChange(event.getFile());
92 public void fileCopied(@NotNull VirtualFileCopyEvent event) {
93 processChange(event.getFile());
97 public void fileMoved(@NotNull VirtualFileMoveEvent event) {
98 processChange(event.getFile());
102 public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) {
103 if (VirtualFile.PROP_NAME.equals(event.getPropertyName()) || VirtualFile.PROP_SYMLINK_TARGET.equals(event.getPropertyName())) {
104 processChange(event.getFile());
109 public void beforeContentsChange(@NotNull VirtualFileEvent event) {
110 processChange(event.getFile());
114 public void beforeFileDeletion(@NotNull VirtualFileEvent event) {
115 processChange(event.getFile());
119 public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) {
120 processChange(event.getFile());
123 private void processChange(VirtualFile file) {
124 if (myReader != null && myProjectFileIndex.isInSourceContent(file) && myFileTypes.contains(file.getFileType())) {
125 final Module module = myProjectFileIndex.getModuleForFile(file);
126 if (module != null) {
127 if (myChangedModules.add(module)) {
128 myDirtyScope = myDirtyScope.union(module.getModuleWithDependentsScope());
139 public void projectClosed() {
145 public GlobalSearchScope getScopeWithoutReferences(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) {
146 if (!isServiceEnabled()) return null;
148 final ParameterizedCachedValueProvider<GlobalSearchScope, CompilerSearchAdapter> cachedValueProvider =
149 new ParameterizedCachedValueProvider<GlobalSearchScope, CompilerSearchAdapter>() {
152 public CachedValueProvider.Result<GlobalSearchScope> compute(CompilerSearchAdapter param) {
153 return CachedValueProvider.Result.create(calculateScopeWithoutReferences(element, param), PsiModificationTracker.MODIFICATION_COUNT);
156 return CachedValuesManager.getManager(myProject).getParameterizedCachedValue(element,
163 private boolean isServiceEnabled() {
164 return myReader != null && isEnabled();
168 private GlobalSearchScope calculateScopeWithoutReferences(@NotNull PsiElement element, CompilerSearchAdapter adapter) {
169 TIntHashSet referentFileIds = getReferentFileIds(element, adapter);
170 if (referentFileIds == null) return null;
172 return getScopeRestrictedByFileTypes(new ScopeWithoutReferencesOnCompilation(referentFileIds).intersectWith(notScope(myDirtyScope)),
173 myFileTypes.toArray(new FileType[myFileTypes.size()]));
177 private TIntHashSet getReferentFileIds(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) {
178 final PsiFile file = element.getContainingFile();
179 if (file == null) return null;
180 final VirtualFile vFile = file.getVirtualFile();
181 if (vFile == null) return null;
183 ElementPlace place = ElementPlace.get(vFile, myProjectFileIndex);
188 if (myDirtyScope.contains(vFile)) {
191 CompilerElement[] compilerElements;
192 if (place == ElementPlace.SRC) {
193 final CompilerElement compilerElement = adapter.asCompilerElement(element);
194 compilerElements = compilerElement == null ? CompilerElement.EMPTY_ARRAY : new CompilerElement[]{compilerElement};
197 compilerElements = adapter.libraryElementAsCompilerElements(element);
199 if (compilerElements.length == 0) return null;
201 synchronized (myLock) {
202 if (myReader == null) return null;
203 TIntHashSet referentFileIds = new TIntHashSet();
204 for (CompilerElement compilerElement : compilerElements) {
205 referentFileIds.addAll(myReader.findReferentFileIds(compilerElement, adapter).toArray());
207 return referentFileIds;
211 private void closeReaderIfNeed() {
212 synchronized (myLock) {
213 if (myReader != null) {
220 private void openReaderIfNeed() {
221 synchronized (myLock) {
222 if (myProject.isOpen()) {
223 myReader = CompilerReferenceReader.create(myProject);
230 public Set<VirtualFile> getReferentFiles(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) {
231 FileBasedIndex fileIndex = FileBasedIndex.getInstance();
232 final TIntHashSet ids = getReferentFileIds(element, adapter);
233 if (ids == null) return null;
234 Set<VirtualFile> fileSet = new THashSet<>();
236 final VirtualFile vFile = fileIndex.findFileById(myProject, id);
237 assert vFile != null;
244 private enum ElementPlace {
247 private static ElementPlace get(VirtualFile file, ProjectFileIndex index) {
248 return index.isInSourceContent(file) ? SRC :
249 ((index.isInLibrarySource(file) || index.isInLibraryClasses(file)) ? LIB : null);
253 private static class ScopeWithoutReferencesOnCompilation extends GlobalSearchScope {
254 private final TIntHashSet myReferentIds;
256 private ScopeWithoutReferencesOnCompilation(TIntHashSet ids) {
261 public boolean contains(@NotNull VirtualFile file) {
262 return !(file instanceof VirtualFileWithId) || !myReferentIds.contains(((VirtualFileWithId)file).getId());
266 public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
271 public boolean isSearchInModuleContent(@NotNull Module aModule) {
276 public boolean isSearchInLibraries() {