ced908fd080c7432519f1d170aeae4ec0033d448
[idea/community.git] / platform / lang-impl / src / com / intellij / psi / impl / cache / impl / IndexCacheManagerImpl.java
1 /*
2  * Copyright 2000-2009 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
17 package com.intellij.psi.impl.cache.impl;
18
19 import com.intellij.injected.editor.VirtualFileWindow;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.application.ReadActionProcessor;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.progress.ProcessCanceledException;
24 import com.intellij.openapi.progress.ProgressManager;
25 import com.intellij.openapi.project.IndexNotReadyException;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.roots.ProjectFileIndex;
28 import com.intellij.openapi.roots.ProjectRootManager;
29 import com.intellij.openapi.vfs.VirtualFile;
30 import com.intellij.psi.PsiFile;
31 import com.intellij.psi.PsiManager;
32 import com.intellij.psi.impl.cache.CacheManager;
33 import com.intellij.psi.impl.cache.impl.id.IdIndex;
34 import com.intellij.psi.impl.cache.impl.id.IdIndexEntry;
35 import com.intellij.psi.impl.cache.impl.todo.TodoIndex;
36 import com.intellij.psi.impl.cache.impl.todo.TodoIndexEntry;
37 import com.intellij.psi.search.GlobalSearchScope;
38 import com.intellij.psi.search.IndexPattern;
39 import com.intellij.psi.search.IndexPatternProvider;
40 import com.intellij.psi.util.PsiUtilCore;
41 import com.intellij.util.CommonProcessors;
42 import com.intellij.util.Processor;
43 import com.intellij.util.indexing.FileBasedIndex;
44 import gnu.trove.THashSet;
45 import org.jetbrains.annotations.NotNull;
46
47 import java.util.Collection;
48 import java.util.HashSet;
49 import java.util.Set;
50
51 /**
52  * @author Eugene Zhuravlev
53  *         Date: Jan 16, 2008
54  */
55 public class IndexCacheManagerImpl implements CacheManager{
56   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.cache.impl.IndexCacheManagerImpl");
57   private final Project myProject;
58   private final PsiManager myPsiManager;
59
60   public IndexCacheManagerImpl(PsiManager psiManager) {
61     myPsiManager = psiManager;
62     myProject = psiManager.getProject();
63   }
64
65   @NotNull
66   public PsiFile[] getFilesWithWord(@NotNull final String word, final short occurenceMask, @NotNull final GlobalSearchScope scope, final boolean caseSensitively) {
67     if (myProject.isDefault()) {
68       return PsiFile.EMPTY_ARRAY;
69     }
70     CommonProcessors.CollectProcessor<PsiFile> processor = new CommonProcessors.CollectProcessor<PsiFile>();
71     processFilesWithWord(processor, word, occurenceMask, scope, caseSensitively);
72     return processor.getResults().isEmpty() ? PsiFile.EMPTY_ARRAY : processor.toArray(PsiFile.EMPTY_ARRAY);
73   }
74
75   public static boolean shouldBeFound(GlobalSearchScope scope, VirtualFile virtualFile, ProjectFileIndex index) {
76     return (scope.isSearchOutsideRootModel() || index.isInContent(virtualFile) || index.isInLibrarySource(virtualFile)) && !virtualFile.getFileType().isBinary();
77   }
78
79   public boolean processFilesWithWord(@NotNull final Processor<PsiFile> psiFileProcessor, @NotNull final String word, final short occurrenceMask, @NotNull final GlobalSearchScope scope, final boolean caseSensitively) {
80     if (myProject.isDefault()) {
81       return true;
82     }
83     final Set<VirtualFile> vFiles = new THashSet<VirtualFile>();
84     final GlobalSearchScope projectScope = GlobalSearchScope.allScope(myProject);
85     try {
86       ApplicationManager.getApplication().runReadAction(new Runnable() {
87         public void run() {
88           FileBasedIndex.getInstance().processValues(IdIndex.NAME, new IdIndexEntry(word, caseSensitively), null, new FileBasedIndex.ValueProcessor<Integer>() {
89             public boolean process(final VirtualFile file, final Integer value) {
90               ProgressManager.checkCanceled();
91               final int mask = value.intValue();
92               if ((mask & occurrenceMask) != 0) {
93                 vFiles.add(file);
94               }
95               return true;
96             }
97           }, projectScope);
98         }
99       });
100     }
101     catch (IndexNotReadyException e) {
102       throw new ProcessCanceledException();
103     }
104
105     if (vFiles.isEmpty()) return true;
106
107     final ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex();
108
109     final Processor<VirtualFile> virtualFileProcessor = new ReadActionProcessor<VirtualFile>() {
110       @Override
111       public boolean processInReadAction(VirtualFile virtualFile) {
112         LOG.assertTrue(virtualFile.isValid());
113         if (virtualFile.isValid() && scope.contains(virtualFile) && shouldBeFound(scope, virtualFile, index)) {
114           final PsiFile psiFile = myPsiManager.findFile(virtualFile);
115           return psiFile == null || psiFileProcessor.process(psiFile);
116         }
117         return true;
118       }
119     };
120
121
122     // IMPORTANT!!!
123     // Since implementation of virtualFileProcessor.process() may call indices directly or indirectly,
124     // we cannot call it inside FileBasedIndex.processValues() method
125     // If we do, deadlocks are possible (IDEADEV-42137). So first we obtain files with the word specified,
126     // and then process them not holding indices' read lock.
127     for (VirtualFile vFile : vFiles) {
128       ProgressManager.checkCanceled();
129       if (!virtualFileProcessor.process(vFile)) {
130         return false;
131       }
132     }
133     return true;
134   }
135
136   @NotNull
137   public PsiFile[] getFilesWithTodoItems() {
138     if (myProject.isDefault()) {
139       return PsiFile.EMPTY_ARRAY;
140     }
141     final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
142     final Set<PsiFile> allFiles = new HashSet<PsiFile>();
143     final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
144     for (IndexPattern indexPattern : IndexPatternUtil.getIndexPatterns()) {
145       final Collection<VirtualFile> files = fileBasedIndex.getContainingFiles(
146         TodoIndex.NAME,
147         new TodoIndexEntry(indexPattern.getPatternString(), indexPattern.isCaseSensitive()), GlobalSearchScope.allScope(myProject));
148       ApplicationManager.getApplication().runReadAction(new Runnable() {
149         public void run() {
150           for (VirtualFile file : files) {
151             if (projectFileIndex.isInContent(file)) {
152               final PsiFile psiFile = myPsiManager.findFile(file);
153               if (psiFile != null) {
154                 allFiles.add(psiFile);
155               }
156             }
157           }
158         }
159       });
160     }
161     return allFiles.isEmpty() ? PsiFile.EMPTY_ARRAY : PsiUtilCore.toPsiFileArray(allFiles);
162   }
163
164   public int getTodoCount(@NotNull final VirtualFile file, final IndexPatternProvider patternProvider) {
165     if (myProject.isDefault()) {
166       return 0;
167     }
168     if (file instanceof VirtualFileWindow) return -1;
169     final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
170     int count = 0;
171     for (IndexPattern indexPattern : patternProvider.getIndexPatterns()) {
172       count += fetchCount(fileBasedIndex, file, indexPattern);
173     }
174     return count;
175   }
176    
177   public int getTodoCount(@NotNull final VirtualFile file, final IndexPattern pattern) {
178     if (myProject.isDefault()) {
179       return 0;
180     }
181     if (file instanceof VirtualFileWindow) return -1;
182     return fetchCount(FileBasedIndex.getInstance(), file, pattern);
183   }
184
185   private int fetchCount(final FileBasedIndex fileBasedIndex, final VirtualFile file, final IndexPattern indexPattern) {
186     final int[] count = {0};
187     fileBasedIndex.processValues(
188       TodoIndex.NAME, new TodoIndexEntry(indexPattern.getPatternString(), indexPattern.isCaseSensitive()), file,
189       new FileBasedIndex.ValueProcessor<Integer>() {
190         public boolean process(final VirtualFile file, final Integer value) {
191           count[0] += value.intValue();
192           return true;
193         }
194       }, GlobalSearchScope.fileScope(myProject, file));
195     return count[0];
196   }
197 }