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