1 package com
.intellij
.util
.indexing
;
3 import com
.intellij
.AppTopics
;
4 import com
.intellij
.concurrency
.Job
;
5 import com
.intellij
.concurrency
.JobScheduler
;
6 import com
.intellij
.ide
.startup
.BackgroundableCacheUpdater
;
7 import com
.intellij
.ide
.startup
.impl
.FileSystemSynchronizerImpl
;
8 import com
.intellij
.lang
.ASTNode
;
9 import com
.intellij
.openapi
.application
.*;
10 import com
.intellij
.openapi
.components
.ApplicationComponent
;
11 import com
.intellij
.openapi
.diagnostic
.Logger
;
12 import com
.intellij
.openapi
.editor
.Document
;
13 import com
.intellij
.openapi
.editor
.highlighter
.EditorHighlighter
;
14 import com
.intellij
.openapi
.editor
.impl
.DocumentImpl
;
15 import com
.intellij
.openapi
.extensions
.Extensions
;
16 import com
.intellij
.openapi
.fileEditor
.FileDocumentManager
;
17 import com
.intellij
.openapi
.fileTypes
.*;
18 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
19 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
20 import com
.intellij
.openapi
.progress
.ProgressManager
;
21 import com
.intellij
.openapi
.progress
.Task
;
22 import com
.intellij
.openapi
.progress
.impl
.BackgroundableProcessIndicator
;
23 import com
.intellij
.openapi
.project
.*;
24 import com
.intellij
.openapi
.roots
.CollectingContentIterator
;
25 import com
.intellij
.openapi
.roots
.ContentIterator
;
26 import com
.intellij
.openapi
.roots
.ProjectRootManager
;
27 import com
.intellij
.openapi
.util
.Key
;
28 import com
.intellij
.openapi
.util
.Pair
;
29 import com
.intellij
.openapi
.util
.ShutDownTracker
;
30 import com
.intellij
.openapi
.util
.io
.FileUtil
;
31 import com
.intellij
.openapi
.util
.registry
.Registry
;
32 import com
.intellij
.openapi
.vfs
.*;
33 import com
.intellij
.openapi
.vfs
.ex
.VirtualFileManagerEx
;
34 import com
.intellij
.openapi
.vfs
.newvfs
.ManagingFS
;
35 import com
.intellij
.openapi
.vfs
.newvfs
.NewVirtualFile
;
36 import com
.intellij
.openapi
.vfs
.newvfs
.persistent
.PersistentFS
;
37 import com
.intellij
.psi
.PsiDocumentManager
;
38 import com
.intellij
.psi
.PsiFile
;
39 import com
.intellij
.psi
.PsiLock
;
40 import com
.intellij
.psi
.SingleRootFileViewProvider
;
41 import com
.intellij
.psi
.impl
.PsiDocumentTransactionListener
;
42 import com
.intellij
.psi
.impl
.source
.PsiFileImpl
;
43 import com
.intellij
.psi
.search
.GlobalSearchScope
;
44 import com
.intellij
.util
.ArrayUtil
;
45 import com
.intellij
.util
.CommonProcessors
;
46 import com
.intellij
.util
.Processor
;
47 import com
.intellij
.util
.concurrency
.JBLock
;
48 import com
.intellij
.util
.concurrency
.JBReentrantReadWriteLock
;
49 import com
.intellij
.util
.concurrency
.LockFactory
;
50 import com
.intellij
.util
.concurrency
.Semaphore
;
51 import com
.intellij
.util
.containers
.ConcurrentHashSet
;
52 import com
.intellij
.util
.containers
.ContainerUtil
;
53 import com
.intellij
.util
.io
.*;
54 import com
.intellij
.util
.messages
.MessageBus
;
55 import com
.intellij
.util
.messages
.MessageBusConnection
;
56 import gnu
.trove
.TIntHashSet
;
57 import gnu
.trove
.TIntIterator
;
58 import gnu
.trove
.TObjectIntHashMap
;
59 import org
.jetbrains
.annotations
.NonNls
;
60 import org
.jetbrains
.annotations
.NotNull
;
61 import org
.jetbrains
.annotations
.Nullable
;
62 import org
.jetbrains
.annotations
.TestOnly
;
67 import java
.util
.concurrent
.atomic
.AtomicBoolean
;
68 import java
.util
.concurrent
.atomic
.AtomicInteger
;
69 import java
.util
.concurrent
.locks
.Lock
;
72 * @author Eugene Zhuravlev
76 public class FileBasedIndex
implements ApplicationComponent
{
77 private static final Logger LOG
= Logger
.getInstance("#com.intellij.util.indexing.FileBasedIndex");
79 private static final String CORRUPTION_MARKER_NAME
= "corruption.marker";
80 private final Map
<ID
<?
, ?
>, Pair
<UpdatableIndex
<?
, ?
, FileContent
>, InputFilter
>> myIndices
= new HashMap
<ID
<?
, ?
>, Pair
<UpdatableIndex
<?
, ?
, FileContent
>, InputFilter
>>();
81 private final Map
<ID
<?
, ?
>, Semaphore
> myUnsavedDataIndexingSemaphores
= new HashMap
<ID
<?
,?
>, Semaphore
>();
82 private final TObjectIntHashMap
<ID
<?
, ?
>> myIndexIdToVersionMap
= new TObjectIntHashMap
<ID
<?
, ?
>>();
83 private final Set
<ID
<?
, ?
>> myNotRequiringContentIndices
= new HashSet
<ID
<?
, ?
>>();
84 private final Set
<FileType
> myNoLimitCheckTypes
= new HashSet
<FileType
>();
86 private final PerIndexDocumentMap
<Long
> myLastIndexedDocStamps
= new PerIndexDocumentMap
<Long
>() {
88 protected Long
createDefault(Document document
) {
93 private final ChangedFilesUpdater myChangedFilesUpdater
;
95 private final List
<IndexableFileSet
> myIndexableSets
= ContainerUtil
.createEmptyCOWList();
97 public static final int OK
= 1;
98 public static final int REQUIRES_REBUILD
= 2;
99 public static final int REBUILD_IN_PROGRESS
= 3;
100 private final Map
<ID
<?
, ?
>, AtomicInteger
> myRebuildStatus
= new HashMap
<ID
<?
,?
>, AtomicInteger
>();
102 private final VirtualFileManagerEx myVfManager
;
103 private final FileDocumentManager myFileDocumentManager
;
104 private final ConcurrentHashSet
<ID
<?
, ?
>> myUpToDateIndices
= new ConcurrentHashSet
<ID
<?
, ?
>>();
105 private final Map
<Document
, PsiFile
> myTransactionMap
= new HashMap
<Document
, PsiFile
>();
107 private static final int ALREADY_PROCESSED
= 0x02;
108 private static final String USE_MULTITHREADED_INDEXING
= "fileIndex.multithreaded";
109 private @Nullable String myConfigPath
;
110 private @Nullable String mySystemPath
;
112 public void requestReindex(final VirtualFile file
) {
113 myChangedFilesUpdater
.invalidateIndices(file
, true);
116 public interface InputFilter
{
117 boolean acceptInput(VirtualFile file
);
120 public FileBasedIndex(final VirtualFileManagerEx vfManager
, FileDocumentManager fdm
, MessageBus bus
) throws IOException
{
121 myVfManager
= vfManager
;
122 myFileDocumentManager
= fdm
;
124 myConfigPath
= calcConfigPath(PathManager
.getConfigPath());
125 mySystemPath
= calcConfigPath(PathManager
.getSystemPath());
127 final MessageBusConnection connection
= bus
.connect();
128 connection
.subscribe(PsiDocumentTransactionListener
.TOPIC
, new PsiDocumentTransactionListener() {
129 public void transactionStarted(final Document doc
, final PsiFile file
) {
131 myTransactionMap
.put(doc
, file
);
132 myUpToDateIndices
.clear();
136 public void transactionCompleted(final Document doc
, final PsiFile file
) {
137 myTransactionMap
.remove(doc
);
141 connection
.subscribe(AppTopics
.FILE_TYPES
, new FileTypeListener() {
142 private Map
<FileType
, Set
<String
>> myTypeToExtensionMap
;
143 public void beforeFileTypesChanged(final FileTypeEvent event
) {
144 cleanupProcessedFlag();
145 myTypeToExtensionMap
= new HashMap
<FileType
, Set
<String
>>();
146 final FileTypeManager manager
= event
.getManager();
147 for (FileType type
: manager
.getRegisteredFileTypes()) {
148 myTypeToExtensionMap
.put(type
, getExtensions(manager
, type
));
152 public void fileTypesChanged(final FileTypeEvent event
) {
153 final Map
<FileType
, Set
<String
>> oldExtensions
= myTypeToExtensionMap
;
154 myTypeToExtensionMap
= null;
155 if (oldExtensions
!= null) {
156 final FileTypeManager manager
= event
.getManager();
157 final Map
<FileType
, Set
<String
>> newExtensions
= new HashMap
<FileType
, Set
<String
>>();
158 for (FileType type
: manager
.getRegisteredFileTypes()) {
159 newExtensions
.put(type
, getExtensions(manager
, type
));
161 // we are interested only in extension changes or removals.
162 // addition of an extension is handled separately by RootsChanged event
163 if (!newExtensions
.keySet().containsAll(oldExtensions
.keySet())) {
167 for (FileType type
: oldExtensions
.keySet()) {
168 if (!newExtensions
.get(type
).containsAll(oldExtensions
.get(type
))) {
176 private Set
<String
> getExtensions(FileTypeManager manager
, FileType type
) {
177 final Set
<String
> set
= new HashSet
<String
>();
178 for (FileNameMatcher matcher
: manager
.getAssociations(type
)) {
179 set
.add(matcher
.getPresentableString());
184 private void rebuildAllndices() {
185 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
186 requestRebuild(indexId
);
191 ApplicationManager
.getApplication().addApplicationListener(new ApplicationAdapter() {
192 public void writeActionStarted(Object action
) {
193 myUpToDateIndices
.clear();
197 final File workInProgressFile
= getMarkerFile();
198 if (workInProgressFile
.exists()) {
199 // previous IDEA session was closed incorrectly, so drop all indices
200 FileUtil
.delete(PathManager
.getIndexRoot());
204 final FileBasedIndexExtension
[] extensions
= Extensions
.getExtensions(FileBasedIndexExtension
.EXTENSION_POINT_NAME
);
205 for (FileBasedIndexExtension
<?
, ?
> extension
: extensions
) {
206 myRebuildStatus
.put(extension
.getName(), new AtomicInteger(OK
));
209 final File corruptionMarker
= new File(PathManager
.getIndexRoot(), CORRUPTION_MARKER_NAME
);
210 final boolean currentVersionCorrupted
= corruptionMarker
.exists();
211 for (FileBasedIndexExtension
<?
, ?
> extension
: extensions
) {
212 registerIndexer(extension
, currentVersionCorrupted
);
214 FileUtil
.delete(corruptionMarker
);
215 dropUnregisteredIndices();
217 // check if rebuild was requested for any index during registration
218 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
219 if (myRebuildStatus
.get(indexId
).compareAndSet(REQUIRES_REBUILD
, OK
)) {
223 catch (StorageException e
) {
224 requestRebuild(indexId
);
230 myChangedFilesUpdater
= new ChangedFilesUpdater();
231 vfManager
.addVirtualFileListener(myChangedFilesUpdater
);
233 vfManager
.registerRefreshUpdater(myChangedFilesUpdater
);
235 registerIndexableSet(new AdditionalIndexableFileSet());
238 ShutDownTracker
.getInstance().registerShutdownTask(new Runnable() {
243 FileUtil
.createIfDoesntExist(workInProgressFile
);
244 saveRegisteredIndices(myIndices
.keySet());
248 private String
calcConfigPath(final String path
) {
250 final String _path
= FileUtil
.toSystemIndependentName(new File(path
).getCanonicalPath());
251 return _path
.endsWith("/")? _path
: _path
+ "/" ;
253 catch (IOException e
) {
259 private static FileBasedIndex ourInstance
= CachedSingletonsRegistry
.markCachedField(FileBasedIndex
.class);
260 public static FileBasedIndex
getInstance() {
261 if (ourInstance
== null) {
262 ourInstance
= ApplicationManager
.getApplication().getComponent(FileBasedIndex
.class);
269 * @return true if registered index requires full rebuild for some reason, e.g. is just created or corrupted @param extension
270 * @param isCurrentVersionCorrupted
272 private <K
, V
> void registerIndexer(final FileBasedIndexExtension
<K
, V
> extension
, final boolean isCurrentVersionCorrupted
) throws IOException
{
273 final ID
<K
, V
> name
= extension
.getName();
274 final int version
= extension
.getVersion();
275 if (!extension
.dependsOnFileContent()) {
276 myNotRequiringContentIndices
.add(name
);
278 myIndexIdToVersionMap
.put(name
, version
);
279 final File versionFile
= IndexInfrastructure
.getVersionFile(name
);
280 if (isCurrentVersionCorrupted
|| IndexInfrastructure
.versionDiffers(versionFile
, version
)) {
281 if (!isCurrentVersionCorrupted
) {
282 LOG
.info("Version has changed for index " + extension
.getName() + ". The index will be rebuilt.");
284 FileUtil
.delete(IndexInfrastructure
.getIndexRootDir(name
));
285 IndexInfrastructure
.rewriteVersion(versionFile
, version
);
288 for (int attempt
= 0; attempt
< 2; attempt
++) {
290 final MapIndexStorage
<K
, V
> storage
= new MapIndexStorage
<K
, V
>(IndexInfrastructure
.getStorageFile(name
), extension
.getKeyDescriptor(), extension
.getValueExternalizer(), extension
.getCacheSize());
291 final IndexStorage
<K
, V
> memStorage
= new MemoryIndexStorage
<K
, V
>(storage
);
292 final UpdatableIndex
<K
, V
, FileContent
> index
= createIndex(name
, extension
, memStorage
);
293 myIndices
.put(name
, new Pair
<UpdatableIndex
<?
,?
, FileContent
>, InputFilter
>(index
, new IndexableFilesFilter(extension
.getInputFilter())));
294 myUnsavedDataIndexingSemaphores
.put(name
, new Semaphore());
295 myNoLimitCheckTypes
.addAll(extension
.getFileTypesWithSizeLimitNotApplicable());
298 catch (IOException e
) {
300 FileUtil
.delete(IndexInfrastructure
.getIndexRootDir(name
));
301 IndexInfrastructure
.rewriteVersion(versionFile
, version
);
306 private static void saveRegisteredIndices(Collection
<ID
<?
, ?
>> ids
) {
307 final File file
= getRegisteredIndicesFile();
309 FileUtil
.createIfDoesntExist(file
);
310 final DataOutputStream os
= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file
)));
312 os
.writeInt(ids
.size());
313 for (ID
<?
, ?
> id
: ids
) {
314 IOUtil
.writeString(id
.toString(), os
);
321 catch (IOException ignored
) {
325 private static Set
<String
> readRegistsredIndexNames() {
326 final Set
<String
> result
= new HashSet
<String
>();
328 final DataInputStream in
= new DataInputStream(new BufferedInputStream(new FileInputStream(getRegisteredIndicesFile())));
330 final int size
= in
.readInt();
331 for (int idx
= 0; idx
< size
; idx
++) {
332 result
.add(IOUtil
.readString(in
));
339 catch (IOException ignored
) {
344 private static File
getRegisteredIndicesFile() {
345 return new File(PathManager
.getIndexRoot(), "registered");
348 private static File
getMarkerFile() {
349 return new File(PathManager
.getIndexRoot(), "work_in_progress");
352 private <K
, V
> UpdatableIndex
<K
, V
, FileContent
> createIndex(final ID
<K
, V
> indexId
, final FileBasedIndexExtension
<K
, V
> extension
, final IndexStorage
<K
, V
> storage
) throws IOException
{
353 final MapReduceIndex
<K
, V
, FileContent
> index
;
354 if (extension
instanceof CustomImplementationFileBasedIndexExtension
) {
355 final UpdatableIndex
<K
, V
, FileContent
> custom
=
356 ((CustomImplementationFileBasedIndexExtension
<K
, V
, FileContent
>)extension
).createIndexImplementation(indexId
, this, storage
);
357 if (!(custom
instanceof MapReduceIndex
)) {
360 index
= (MapReduceIndex
<K
,V
, FileContent
>)custom
;
363 index
= new MapReduceIndex
<K
, V
, FileContent
>(indexId
, extension
.getIndexer(), storage
);
366 final KeyDescriptor
<K
> keyDescriptor
= extension
.getKeyDescriptor();
367 index
.setInputIdToDataKeysIndex(new PersistentHashMap
<Integer
, Collection
<K
>>(IndexInfrastructure
.getInputIndexStorageFile(indexId
), new EnumeratorIntegerDescriptor(), new DataExternalizer
<Collection
<K
>>() {
368 public void save(DataOutput out
, Collection
<K
> value
) throws IOException
{
369 DataInputOutputUtil
.writeINT(out
, value
.size());
370 for (K key
: value
) {
371 keyDescriptor
.save(out
, key
);
375 public Collection
<K
> read(DataInput in
) throws IOException
{
376 final int size
= DataInputOutputUtil
.readINT(in
);
377 final List
<K
> list
= new ArrayList
<K
>();
378 for (int idx
= 0; idx
< size
; idx
++) {
379 list
.add(keyDescriptor
.read(in
));
390 public String
getComponentName() {
391 return "FileBasedIndex";
394 public void initComponent() {
397 public void disposeComponent() {
401 private AtomicBoolean myShutdownPerformed
= new AtomicBoolean(false);
403 private void performShutdown() {
404 if (!myShutdownPerformed
.compareAndSet(false, true)) {
405 return; // already shut down
407 myFileDocumentManager
.saveAllDocuments();
409 LOG
.info("START INDEX SHUTDOWN");
411 myChangedFilesUpdater
.forceUpdate();
413 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
414 final UpdatableIndex
<?
, ?
, FileContent
> index
= getIndex(indexId
);
415 assert index
!= null;
416 checkRebuild(indexId
, true); // if the index was scheduled for rebuild, only clean it
417 //LOG.info("DISPOSING " + indexId);
421 myVfManager
.removeVirtualFileListener(myChangedFilesUpdater
);
422 myVfManager
.unregisterRefreshUpdater(myChangedFilesUpdater
);
424 FileUtil
.delete(getMarkerFile());
426 catch (Throwable e
) {
427 LOG
.info("Problems during index shutdown", e
);
428 throw new RuntimeException(e
);
430 LOG
.info("END INDEX SHUTDOWN");
433 public void flushCaches() {
434 IndexingStamp
.flushCache();
435 ApplicationManager
.getApplication().executeOnPooledThread(new Runnable() {
437 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
438 //noinspection ConstantConditions
440 getIndex(indexId
).flush();
442 catch (StorageException e
) {
444 requestRebuild(indexId
);
452 public <K
> Collection
<K
> getAllKeys(final ID
<K
, ?
> indexId
, @NotNull Project project
) {
453 Set
<K
> allKeys
= new HashSet
<K
>();
454 processAllKeys(indexId
, new CommonProcessors
.CollectProcessor
<K
>(allKeys
), project
);
458 public <K
> boolean processAllKeys(final ID
<K
, ?
> indexId
, Processor
<K
> processor
, @NotNull Project project
) {
460 ensureUpToDate(indexId
, project
);
461 final UpdatableIndex
<K
, ?
, FileContent
> index
= getIndex(indexId
);
462 if (index
== null) return true;
463 return index
.processAllKeys(processor
);
465 catch (StorageException e
) {
466 scheduleRebuild(indexId
, e
);
468 catch (RuntimeException e
) {
469 final Throwable cause
= e
.getCause();
470 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
471 scheduleRebuild(indexId
, cause
);
481 private static final ThreadLocal
<Integer
> myUpToDateCheckState
= new ThreadLocal
<Integer
>();
483 public void disableUpToDateCheckForCurrentThread() {
484 final Integer currentValue
= myUpToDateCheckState
.get();
485 myUpToDateCheckState
.set(currentValue
== null?
1 : currentValue
.intValue() + 1);
488 public void enableUpToDateCheckForCurrentThread() {
489 final Integer currentValue
= myUpToDateCheckState
.get();
490 if (currentValue
!= null) {
491 final int newValue
= currentValue
.intValue() - 1;
493 myUpToDateCheckState
.set(newValue
);
496 myUpToDateCheckState
.remove();
501 private boolean isUpToDateCheckEnabled() {
502 final Integer value
= myUpToDateCheckState
.get();
503 return value
== null || value
.intValue() == 0;
507 private final ThreadLocal
<Boolean
> myReentrancyGuard
= new ThreadLocal
<Boolean
>() {
508 protected Boolean
initialValue() {
509 return Boolean
.FALSE
;
514 * DO NOT CALL DIRECTLY IN CLIENT CODE
515 * The method is internal to indexing engine end is called internally. The method is public due to implementation details
517 public <K
> void ensureUpToDate(final ID
<K
, ?
> indexId
, @NotNull Project project
) {
518 if (isDumb(project
)) {
519 handleDumbMode(indexId
, project
);
522 if (myReentrancyGuard
.get().booleanValue()) {
523 //assert false : "ensureUpToDate() is not reentrant!";
526 myReentrancyGuard
.set(Boolean
.TRUE
);
529 if (isUpToDateCheckEnabled()) {
531 checkRebuild(indexId
, false);
532 indexUnsavedDocuments(indexId
, project
);
534 catch (StorageException e
) {
535 scheduleRebuild(indexId
, e
);
537 catch (RuntimeException e
) {
538 final Throwable cause
= e
.getCause();
539 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
540 scheduleRebuild(indexId
, e
);
549 myReentrancyGuard
.set(Boolean
.FALSE
);
553 private void handleDumbMode(final ID
<?
, ?
> indexId
, Project project
) {
554 if (myNotRequiringContentIndices
.contains(indexId
)) {
555 return; //indexed eagerly in foreground while building unindexed file list
558 final ProgressManager progressManager
= ProgressManager
.getInstance();
559 progressManager
.checkCanceled(); // DumbModeAction.CANCEL
561 final ProgressIndicator progressIndicator
= progressManager
.getProgressIndicator();
562 if (progressIndicator
instanceof BackgroundableProcessIndicator
) {
563 final BackgroundableProcessIndicator indicator
= (BackgroundableProcessIndicator
)progressIndicator
;
564 if (indicator
.getDumbModeAction() == DumbModeAction
.WAIT
) {
565 assert !ApplicationManager
.getApplication().isDispatchThread();
566 DumbService
.getInstance(project
).waitForSmartMode();
571 throw new IndexNotReadyException();
574 private static boolean isDumb(Project project
) {
575 return DumbServiceImpl
.getInstance(project
).isDumb();
579 public <K
, V
> List
<V
> getValues(final ID
<K
, V
> indexId
, @NotNull K dataKey
, @NotNull final GlobalSearchScope filter
) {
580 final List
<V
> values
= new ArrayList
<V
>();
581 processValuesImpl(indexId
, dataKey
, true, null, new ValueProcessor
<V
>() {
582 public boolean process(final VirtualFile file
, final V value
) {
591 public <K
, V
> Collection
<VirtualFile
> getContainingFiles(final ID
<K
, V
> indexId
, @NotNull K dataKey
, @NotNull final GlobalSearchScope filter
) {
592 final Set
<VirtualFile
> files
= new HashSet
<VirtualFile
>();
593 processValuesImpl(indexId
, dataKey
, false, null, new ValueProcessor
<V
>() {
594 public boolean process(final VirtualFile file
, final V value
) {
603 public interface ValueProcessor
<V
> {
605 * @param value a value to process
606 * @param file the file the value came from
607 * @return false if no further processing is needed, true otherwise
609 boolean process(VirtualFile file
, V value
);
613 * @return false if ValueProcessor.process() returned false; true otherwise or if ValueProcessor was not called at all
615 public <K
, V
> boolean processValues(final ID
<K
, V
> indexId
, final @NotNull K dataKey
, @Nullable final VirtualFile inFile
,
616 ValueProcessor
<V
> processor
, @NotNull final GlobalSearchScope filter
) {
617 return processValuesImpl(indexId
, dataKey
, false, inFile
, processor
, filter
);
622 private <K
, V
> boolean processValuesImpl(final ID
<K
, V
> indexId
, final K dataKey
, boolean ensureValueProcessedOnce
,
623 @Nullable final VirtualFile restrictToFile
, ValueProcessor
<V
> processor
,
624 final GlobalSearchScope filter
) {
626 final Project project
= filter
.getProject();
627 assert project
!= null : "GlobalSearchScope#getProject() should be not-null for all index queries";
628 ensureUpToDate(indexId
, project
);
629 final UpdatableIndex
<K
, V
, FileContent
> index
= getIndex(indexId
);
634 final Lock readLock
= index
.getReadLock();
637 final ValueContainer
<V
> container
= index
.getData(dataKey
);
639 boolean shouldContinue
= true;
641 if (restrictToFile
!= null) {
642 if (restrictToFile
instanceof VirtualFileWithId
) {
643 final int restrictedFileId
= getFileId(restrictToFile
);
644 for (final Iterator
<V
> valueIt
= container
.getValueIterator(); valueIt
.hasNext();) {
645 final V value
= valueIt
.next();
646 if (container
.isAssociated(value
, restrictedFileId
)) {
647 shouldContinue
= processor
.process(restrictToFile
, value
);
648 if (!shouldContinue
) {
656 final PersistentFS fs
= (PersistentFS
)ManagingFS
.getInstance();
657 VALUES_LOOP
: for (final Iterator
<V
> valueIt
= container
.getValueIterator(); valueIt
.hasNext();) {
658 final V value
= valueIt
.next();
659 for (final ValueContainer
.IntIterator inputIdsIterator
= container
.getInputIdsIterator(value
); inputIdsIterator
.hasNext();) {
660 final int id
= inputIdsIterator
.next();
661 VirtualFile file
= IndexInfrastructure
.findFileById(fs
, id
);
662 if (file
!= null && filter
.accept(file
)) {
663 shouldContinue
= processor
.process(file
, value
);
664 if (!shouldContinue
) {
667 if (ensureValueProcessedOnce
) {
668 break; // continue with the next value
674 return shouldContinue
;
677 index
.getReadLock().unlock();
680 catch (StorageException e
) {
681 scheduleRebuild(indexId
, e
);
683 catch (RuntimeException e
) {
684 final Throwable cause
= e
.getCause();
685 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
686 scheduleRebuild(indexId
, cause
);
695 public <K
, V
> boolean getFilesWithKey(final ID
<K
, V
> indexId
, final Set
<K
> dataKeys
,
696 Processor
<VirtualFile
> processor
,
697 GlobalSearchScope filter
) {
699 final Project project
= filter
.getProject();
700 assert project
!= null : "GlobalSearchScope#getProject() should be not-null for all index queries";
701 ensureUpToDate(indexId
, project
);
702 final UpdatableIndex
<K
, V
, FileContent
> index
= getIndex(indexId
);
707 final Lock readLock
= index
.getReadLock();
710 List
<TIntHashSet
> locals
= new ArrayList
<TIntHashSet
>();
711 for (K dataKey
: dataKeys
) {
712 TIntHashSet local
= new TIntHashSet();
714 final ValueContainer
<V
> container
= index
.getData(dataKey
);
716 for (final Iterator
<V
> valueIt
= container
.getValueIterator(); valueIt
.hasNext();) {
717 final V value
= valueIt
.next();
718 for (final ValueContainer
.IntIterator inputIdsIterator
= container
.getInputIdsIterator(value
); inputIdsIterator
.hasNext();) {
719 final int id
= inputIdsIterator
.next();
725 if (locals
.size() == 0) return true;
727 Collections
.sort(locals
, new Comparator
<TIntHashSet
>() {
728 public int compare(TIntHashSet o1
, TIntHashSet o2
) {
729 return o1
.size() - o2
.size();
733 final PersistentFS fs
= (PersistentFS
)ManagingFS
.getInstance();
734 TIntIterator ids
= join(locals
).iterator();
735 while (ids
.hasNext()) {
737 VirtualFile file
= IndexInfrastructure
.findFileById(fs
, id
);
738 if (file
!= null && filter
.accept(file
)) {
739 if (!processor
.process(file
)) return false;
744 index
.getReadLock().unlock();
747 catch (StorageException e
) {
748 scheduleRebuild(indexId
, e
);
750 catch (RuntimeException e
) {
751 final Throwable cause
= e
.getCause();
752 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
753 scheduleRebuild(indexId
, cause
);
762 private static TIntHashSet
join(List
<TIntHashSet
> locals
) {
763 TIntHashSet result
= locals
.get(0);
764 if (locals
.size() > 1) {
765 TIntIterator it
= result
.iterator();
767 while (it
.hasNext()) {
769 for (int i
= 1; i
< locals
.size(); i
++) {
770 if (!locals
.get(i
).contains(id
)) {
780 public interface AllValuesProcessor
<V
> {
781 void process(final int inputId
, V value
);
784 public <K
, V
> void processAllValues(final ID
<K
, V
> indexId
, AllValuesProcessor
<V
> processor
, @NotNull Project project
) {
786 ensureUpToDate(indexId
, project
);
787 final UpdatableIndex
<K
, V
, FileContent
> index
= getIndex(indexId
);
792 index
.getReadLock().lock();
793 for (K dataKey
: index
.getAllKeys()) {
794 final ValueContainer
<V
> container
= index
.getData(dataKey
);
795 for (final Iterator
<V
> it
= container
.getValueIterator(); it
.hasNext();) {
796 final V value
= it
.next();
797 for (final ValueContainer
.IntIterator inputsIt
= container
.getInputIdsIterator(value
); inputsIt
.hasNext();) {
798 processor
.process(inputsIt
.next(), value
);
804 index
.getReadLock().unlock();
807 catch (StorageException e
) {
808 scheduleRebuild(indexId
, e
);
810 catch (RuntimeException e
) {
811 final Throwable cause
= e
.getCause();
812 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
813 scheduleRebuild(indexId
, e
);
821 private <K
> void scheduleRebuild(final ID
<K
, ?
> indexId
, final Throwable e
) {
822 requestRebuild(indexId
);
824 checkRebuild(indexId
, false);
828 public boolean isIndexReady(final ID
<?
, ?
> indexId
) {
829 return myRebuildStatus
.get(indexId
).get() == OK
;
832 private void checkRebuild(final ID
<?
, ?
> indexId
, final boolean cleanupOnly
) {
833 if (myRebuildStatus
.get(indexId
).compareAndSet(REQUIRES_REBUILD
, REBUILD_IN_PROGRESS
)) {
834 cleanupProcessedFlag();
836 final Runnable rebuildRunnable
= new Runnable() {
841 final FileSystemSynchronizerImpl synchronizer
= new FileSystemSynchronizerImpl();
842 synchronizer
.setCancelable(false);
843 for (Project project
: ProjectManager
.getInstance().getOpenProjects()) {
844 synchronizer
.registerCacheUpdater(new UnindexedFilesUpdater(project
, ProjectRootManager
.getInstance(project
), FileBasedIndex
.this));
846 synchronizer
.executeFileUpdate();
849 catch (StorageException e
) {
850 requestRebuild(indexId
);
854 myRebuildStatus
.get(indexId
).compareAndSet(REBUILD_IN_PROGRESS
, OK
);
859 final Application application
= ApplicationManager
.getApplication();
860 if (cleanupOnly
|| application
.isUnitTestMode()) {
861 rebuildRunnable
.run();
864 SwingUtilities
.invokeLater(new Runnable() {
866 new Task
.Modal(null, "Updating index", false) {
867 public void run(@NotNull final ProgressIndicator indicator
) {
868 indicator
.setIndeterminate(true);
869 rebuildRunnable
.run();
877 if (myRebuildStatus
.get(indexId
).get() == REBUILD_IN_PROGRESS
) {
878 throw new ProcessCanceledException();
882 private void clearIndex(final ID
<?
, ?
> indexId
) throws StorageException
{
883 final UpdatableIndex
<?
, ?
, FileContent
> index
= getIndex(indexId
);
884 assert index
!= null;
887 IndexInfrastructure
.rewriteVersion(IndexInfrastructure
.getVersionFile(indexId
), myIndexIdToVersionMap
.get(indexId
));
889 catch (IOException e
) {
894 private Set
<Document
> getUnsavedOrTransactedDocuments() {
895 Set
<Document
> docs
= new HashSet
<Document
>(Arrays
.asList(myFileDocumentManager
.getUnsavedDocuments()));
896 docs
.addAll(myTransactionMap
.keySet());
900 private void indexUnsavedDocuments(ID
<?
, ?
> indexId
, Project project
) throws StorageException
{
901 myChangedFilesUpdater
.forceUpdate();
903 if (myUpToDateIndices
.contains(indexId
)) {
904 return; // no need to index unsaved docs
907 final Set
<Document
> documents
= getUnsavedOrTransactedDocuments();
908 if (!documents
.isEmpty()) {
909 // now index unsaved data
910 final StorageGuard
.Holder guard
= setDataBufferingEnabled(true);
912 final Semaphore semaphore
= myUnsavedDataIndexingSemaphores
.get(indexId
);
915 for (Document document
: documents
) {
916 indexUnsavedDocument(document
, indexId
, project
);
922 while (!semaphore
.waitFor(500)) { // may need to wait until another thread is done with indexing
923 if (Thread
.holdsLock(PsiLock
.LOCK
)) {
924 break; // hack. Most probably that other indexing threads is waiting for PsiLock, which we're are holding.
927 myUpToDateIndices
.add(indexId
); // safe to set the flag here, becase it will be cleared under the WriteAction
936 private interface DocumentContent
{
938 long getModificationStamp();
941 private static class AuthenticContent
implements DocumentContent
{
942 private final Document myDocument
;
944 private AuthenticContent(final Document document
) {
945 myDocument
= document
;
948 public String
getText() {
949 return myDocument
.getText();
952 public long getModificationStamp() {
953 return myDocument
.getModificationStamp();
957 private static class PsiContent
implements DocumentContent
{
958 private final Document myDocument
;
959 private final PsiFile myFile
;
961 private PsiContent(final Document document
, final PsiFile file
) {
962 myDocument
= document
;
966 public String
getText() {
967 if (myFile
.getModificationStamp() != myDocument
.getModificationStamp()) {
968 final ASTNode node
= myFile
.getNode();
970 return node
.getText();
972 return myDocument
.getText();
975 public long getModificationStamp() {
976 return myFile
.getModificationStamp();
980 private void indexUnsavedDocument(final Document document
, final ID
<?
, ?
> requestedIndexId
, Project project
) throws StorageException
{
981 final VirtualFile vFile
= myFileDocumentManager
.getFile(document
);
982 if (!(vFile
instanceof VirtualFileWithId
) || !vFile
.isValid()) {
986 final PsiFile dominantContentFile
= findDominantPsiForDocument(document
, project
);
988 DocumentContent content
;
989 if (dominantContentFile
!= null && dominantContentFile
.getModificationStamp() != document
.getModificationStamp()) {
990 content
= new PsiContent(document
, dominantContentFile
);
993 content
= new AuthenticContent(document
);
996 final long currentDocStamp
= content
.getModificationStamp();
997 if (currentDocStamp
!= myLastIndexedDocStamps
.getAndSet(document
, requestedIndexId
, currentDocStamp
).longValue()) {
998 final FileContent newFc
= new FileContent(vFile
, content
.getText(), vFile
.getCharset());
1000 if (dominantContentFile
!= null) {
1001 dominantContentFile
.putUserData(PsiFileImpl
.BUILDING_STUB
, true);
1002 newFc
.putUserData(PSI_FILE
, dominantContentFile
);
1005 if (content
instanceof AuthenticContent
) {
1006 newFc
.putUserData(EDITOR_HIGHLIGHTER
, document
instanceof DocumentImpl
1007 ?
((DocumentImpl
)document
).getEditorHighlighterForCachesBuilding() : null);
1010 if (getInputFilter(requestedIndexId
).acceptInput(vFile
)) {
1011 newFc
.putUserData(PROJECT
, project
);
1012 final int inputId
= Math
.abs(getFileId(vFile
));
1013 getIndex(requestedIndexId
).update(inputId
, newFc
);
1016 if (dominantContentFile
!= null) {
1017 dominantContentFile
.putUserData(PsiFileImpl
.BUILDING_STUB
, null);
1022 public static final Key
<PsiFile
> PSI_FILE
= new Key
<PsiFile
>("PSI for stubs");
1023 public static final Key
<EditorHighlighter
> EDITOR_HIGHLIGHTER
= new Key
<EditorHighlighter
>("Editor");
1024 public static final Key
<Project
> PROJECT
= new Key
<Project
>("Context project");
1025 public static final Key
<VirtualFile
> VIRTUAL_FILE
= new Key
<VirtualFile
>("Context virtual file");
1028 private PsiFile
findDominantPsiForDocument(final Document document
, Project project
) {
1029 if (myTransactionMap
.containsKey(document
)) {
1030 return myTransactionMap
.get(document
);
1033 return findLatestKnownPsiForUncomittedDocument(document
, project
);
1036 private final StorageGuard myStorageLock
= new StorageGuard();
1038 private StorageGuard
.Holder
setDataBufferingEnabled(final boolean enabled
) {
1039 final StorageGuard
.Holder holder
= myStorageLock
.enter(enabled
);
1041 synchronized (myLastIndexedDocStamps
) {
1042 myLastIndexedDocStamps
.clear();
1045 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
1046 final MapReduceIndex index
= (MapReduceIndex
)getIndex(indexId
);
1047 assert index
!= null;
1048 final IndexStorage indexStorage
= index
.getStorage();
1049 ((MemoryIndexStorage
)indexStorage
).setBufferingEnabled(enabled
);
1054 private void dropUnregisteredIndices() {
1055 final Set
<String
> indicesToDrop
= readRegistsredIndexNames();
1056 for (ID
<?
, ?
> key
: myIndices
.keySet()) {
1057 indicesToDrop
.remove(key
.toString());
1059 for (String s
: indicesToDrop
) {
1060 FileUtil
.delete(IndexInfrastructure
.getIndexRootDir(ID
.create(s
)));
1064 public void requestRebuild(ID
<?
, ?
> indexId
) {
1065 cleanupProcessedFlag();
1066 LOG
.info("Rebuild requested for index " + indexId
, new Throwable());
1067 myRebuildStatus
.get(indexId
).set(REQUIRES_REBUILD
);
1070 private <K
, V
> UpdatableIndex
<K
, V
, FileContent
> getIndex(ID
<K
, V
> indexId
) {
1071 final Pair
<UpdatableIndex
<?
, ?
, FileContent
>, InputFilter
> pair
= myIndices
.get(indexId
);
1072 //noinspection unchecked
1073 return pair
!= null?
(UpdatableIndex
<K
,V
, FileContent
>)pair
.getFirst() : null;
1076 private InputFilter
getInputFilter(ID
<?
, ?
> indexId
) {
1077 final Pair
<UpdatableIndex
<?
, ?
, FileContent
>, InputFilter
> pair
= myIndices
.get(indexId
);
1078 return pair
!= null? pair
.getSecond() : null;
1081 public void indexFileContent(com
.intellij
.ide
.startup
.FileContent content
) {
1082 final VirtualFile file
= content
.getVirtualFile();
1083 FileContent fc
= null;
1085 PsiFile psiFile
= null;
1087 final List
<Runnable
> tasks
= new ArrayList
<Runnable
>();
1088 for (final ID
<?
, ?
> indexId
: myIndices
.keySet()) {
1089 if (shouldIndexFile(file
, indexId
)) {
1091 byte[] currentBytes
;
1093 currentBytes
= content
.getBytes();
1095 catch (IOException e
) {
1096 currentBytes
= ArrayUtil
.EMPTY_BYTE_ARRAY
;
1098 fc
= new FileContent(file
, currentBytes
);
1100 psiFile
= content
.getUserData(PSI_FILE
);
1101 if (psiFile
!= null) {
1102 psiFile
.putUserData(PsiFileImpl
.BUILDING_STUB
, true);
1103 fc
.putUserData(PSI_FILE
, psiFile
);
1105 Project project
= content
.getUserData(PROJECT
);
1106 if (project
== null) {
1107 project
= ProjectUtil
.guessProjectForFile(file
);
1109 fc
.putUserData(PROJECT
, project
);
1112 final FileContent _fc
= fc
;
1113 tasks
.add(new Runnable() {
1116 updateSingleIndex(indexId
, file
, _fc
);
1118 catch (ProcessCanceledException ignored
) {
1120 catch (StorageException e
) {
1121 requestRebuild(indexId
);
1129 if (tasks
.size() > 0) {
1130 if (Registry
.get(USE_MULTITHREADED_INDEXING
).asBoolean()) {
1131 final Job
<Object
> job
= JobScheduler
.getInstance().createJob("IndexJob", Job
.DEFAULT_PRIORITY
/ 2);
1133 for (Runnable task
: tasks
) {
1136 job
.scheduleAndWaitForResults();
1138 catch (Throwable throwable
) {
1139 LOG
.info(throwable
);
1143 for (Runnable task
: tasks
) {
1149 if (psiFile
!= null) {
1150 psiFile
.putUserData(PsiFileImpl
.BUILDING_STUB
, null);
1154 private void updateSingleIndex(final ID
<?
, ?
> indexId
, final VirtualFile file
, final FileContent currentFC
)
1155 throws StorageException
{
1156 if (myRebuildStatus
.get(indexId
).get() == REQUIRES_REBUILD
) {
1157 return; // the index is scheduled for rebuild, no need to update
1160 final StorageGuard
.Holder lock
= setDataBufferingEnabled(false);
1163 final int inputId
= Math
.abs(getFileId(file
));
1165 final UpdatableIndex
<?
, ?
, FileContent
> index
= getIndex(indexId
);
1166 assert index
!= null;
1168 index
.update(inputId
, currentFC
);
1169 ApplicationManager
.getApplication().runReadAction(new Runnable() {
1171 if (file
.isValid()) {
1172 if (currentFC
!= null) {
1173 IndexingStamp
.update(file
, indexId
, IndexInfrastructure
.getIndexCreationStamp(indexId
));
1176 // mark the file as unindexed
1177 IndexingStamp
.update(file
, indexId
, -1L);
1188 public static int getFileId(final VirtualFile file
) {
1189 if (file
instanceof VirtualFileWithId
) {
1190 return ((VirtualFileWithId
)file
).getId();
1193 throw new IllegalArgumentException("Virtual file doesn't support id: " + file
+ ", implementation class: " + file
.getClass().getName());
1196 private boolean needsFileContentLoading(ID
<?
, ?
> indexId
) {
1197 return !myNotRequiringContentIndices
.contains(indexId
);
1200 private final class ChangedFilesUpdater
extends VirtualFileAdapter
implements BackgroundableCacheUpdater
{
1201 private final Set
<VirtualFile
> myFilesToUpdate
= new LinkedHashSet
<VirtualFile
>();
1202 private final JBReentrantReadWriteLock myLock
= LockFactory
.createReadWriteLock();
1203 private final JBLock r
= myLock
.readLock();
1204 private final JBLock w
= myLock
.writeLock();
1206 private final ManagingFS myManagingFS
= ManagingFS
.getInstance();
1207 // No need to react on movement events since files stay valid, their ids don't change and all associated attributes remain intact.
1209 public void fileCreated(final VirtualFileEvent event
) {
1213 public void fileDeleted(final VirtualFileEvent event
) {
1216 myFilesToUpdate
.remove(event
.getFile()); // no need to update it anymore
1223 public void fileCopied(final VirtualFileCopyEvent event
) {
1227 public void beforeFileDeletion(final VirtualFileEvent event
) {
1228 invalidateIndices(event
.getFile(), false);
1231 public void beforeContentsChange(final VirtualFileEvent event
) {
1232 invalidateIndices(event
.getFile(), true);
1235 public void contentsChanged(final VirtualFileEvent event
) {
1239 public void beforePropertyChange(final VirtualFilePropertyEvent event
) {
1240 if (event
.getPropertyName().equals(VirtualFile
.PROP_NAME
)) {
1241 // indexes may depend on file name
1242 final VirtualFile file
= event
.getFile();
1243 if (!file
.isDirectory()) {
1244 // name change may lead to filetype change so the file might become not indexable
1245 // in general case have to 'unindex' the file and index it again if needed after the name has been changed
1246 invalidateIndices(file
, false);
1251 public void propertyChanged(final VirtualFilePropertyEvent event
) {
1252 if (event
.getPropertyName().equals(VirtualFile
.PROP_NAME
)) {
1253 // indexes may depend on file name
1254 if (!event
.getFile().isDirectory()) {
1260 private void markDirty(final VirtualFileEvent event
) {
1261 cleanProcessedFlag(event
.getFile());
1262 iterateIndexableFiles(event
.getFile(), new Processor
<VirtualFile
>() {
1263 public boolean process(final VirtualFile file
) {
1264 FileContent fileContent
= null;
1265 // handle 'content-less' indices separately
1266 for (ID
<?
, ?
> indexId
: myNotRequiringContentIndices
) {
1267 if (getInputFilter(indexId
).acceptInput(file
)) {
1269 if (fileContent
== null) {
1270 fileContent
= new FileContent(file
);
1272 updateSingleIndex(indexId
, file
, fileContent
);
1274 catch (StorageException e
) {
1276 requestRebuild(indexId
);
1280 // For 'normal indices' schedule the file for update and stop iteration if at least one index accepts it
1281 if (!isTooLarge(file
)) {
1282 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
1283 if (needsFileContentLoading(indexId
) && getInputFilter(indexId
).acceptInput(file
)) {
1286 myFilesToUpdate
.add(file
);
1287 break; // no need to iterate further, as the file is already marked
1299 IndexingStamp
.flushCache();
1302 void invalidateIndices(final VirtualFile file
, final boolean markForReindex
) {
1303 if (isUnderConfigOrSystem(file
)) {
1306 if (file
.isDirectory()) {
1307 if (isMock(file
) || myManagingFS
.wereChildrenAccessed(file
)) {
1308 for (VirtualFile child
: file
.getChildren()) {
1309 invalidateIndices(child
, markForReindex
);
1314 cleanProcessedFlag(file
);
1315 IndexingStamp
.flushCache();
1316 boolean indicesAffected
= false;
1318 final boolean isTooLarge
= isTooLarge(file
);
1319 for (final ID
<?
, ?
> indexId
: myIndices
.keySet()) {
1321 if (myNotRequiringContentIndices
.contains(indexId
)) {
1322 if (shouldUpdateIndex(file
, indexId
)) {
1323 updateSingleIndex(indexId
, file
, null);
1326 else { // the index requires file content
1327 if (!isTooLarge
&& shouldUpdateIndex(file
, indexId
)) {
1328 indicesAffected
= true;
1329 if (markForReindex
) {
1330 // only mark the file as unindexed, reindex will be done lazily
1331 ApplicationManager
.getApplication().runReadAction(new Runnable() {
1333 IndexingStamp
.update(file
, indexId
, -1L);
1338 updateSingleIndex(indexId
, file
, null);
1343 catch (StorageException e
) {
1345 requestRebuild(indexId
);
1348 IndexingStamp
.flushCache();
1349 if (indicesAffected
&& markForReindex
) {
1350 iterateIndexableFiles(file
, new Processor
<VirtualFile
>() {
1351 public boolean process(final VirtualFile file
) {
1354 myFilesToUpdate
.add(file
);
1366 private void iterateIndexableFiles(final VirtualFile file
, final Processor
<VirtualFile
> processor
) {
1367 if (file
.isDirectory()) {
1368 final ContentIterator iterator
= new ContentIterator() {
1369 public boolean processFile(final VirtualFile fileOrDir
) {
1370 if (!fileOrDir
.isDirectory()) {
1371 processor
.process(fileOrDir
);
1377 for (IndexableFileSet set
: myIndexableSets
) {
1378 set
.iterateIndexableFilesIn(file
, iterator
);
1382 for (IndexableFileSet set
: myIndexableSets
) {
1383 if (set
.isInSet(file
)) {
1384 processor
.process(file
);
1391 public boolean initiallyBackgrounded() {
1392 if (ApplicationManager
.getApplication().isCommandLine() || ApplicationManager
.getApplication().isUnitTestMode()) {
1395 return Registry
.get(DumbServiceImpl
.FILE_INDEX_BACKGROUND
).asBoolean();
1398 public boolean canBeSentToBackground(Collection
<VirtualFile
> remaining
) {
1402 public void backgrounded(Collection
<VirtualFile
> remaining
) {
1403 new BackgroundCacheUpdaterRunner(remaining
).processFiles(this);
1406 public VirtualFile
[] queryNeededFiles() {
1409 if (myFilesToUpdate
.isEmpty()) return VirtualFile
.EMPTY_ARRAY
;
1410 return myFilesToUpdate
.toArray(new VirtualFile
[myFilesToUpdate
.size()]);
1417 public void processFile(final com
.intellij
.ide
.startup
.FileContent fileContent
) {
1418 processFileImpl(fileContent
);
1421 private final Semaphore myForceUpdateSemaphore
= new Semaphore();
1423 public void forceUpdate() {
1424 final VirtualFile
[] files
= queryNeededFiles();
1425 if (files
.length
> 0) {
1426 myForceUpdateSemaphore
.down();
1428 for (VirtualFile file
: files
) {
1429 processFileImpl(new com
.intellij
.ide
.startup
.FileContent(file
));
1433 myForceUpdateSemaphore
.up();
1434 myForceUpdateSemaphore
.waitFor(); // possibly wait until another thread completes indexing
1439 public void updatingDone() {
1442 public void canceled() {
1445 private void processFileImpl(final com
.intellij
.ide
.startup
.FileContent fileContent
) {
1446 final VirtualFile file
= fileContent
.getVirtualFile();
1447 final boolean reallyRemoved
;
1450 reallyRemoved
= myFilesToUpdate
.remove(file
);
1455 if (reallyRemoved
&& file
.isValid()) {
1456 indexFileContent(fileContent
);
1457 IndexingStamp
.flushCache();
1462 private class UnindexedFilesFinder
implements CollectingContentIterator
{
1463 private final List
<VirtualFile
> myFiles
= new ArrayList
<VirtualFile
>();
1464 private final ProgressIndicator myProgressIndicator
;
1466 private UnindexedFilesFinder() {
1467 myProgressIndicator
= ProgressManager
.getInstance().getProgressIndicator();
1470 public List
<VirtualFile
> getFiles() {
1474 public boolean processFile(final VirtualFile file
) {
1475 if (!file
.isDirectory()) {
1476 if (file
instanceof NewVirtualFile
&& ((NewVirtualFile
)file
).getFlag(ALREADY_PROCESSED
)) {
1480 if (file
instanceof VirtualFileWithId
) {
1481 boolean oldStuff
= true;
1482 if (!isTooLarge(file
)) {
1483 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
1485 if (shouldIndexFile(file
, indexId
)) {
1491 catch (RuntimeException e
) {
1492 final Throwable cause
= e
.getCause();
1493 if (cause
instanceof IOException
|| cause
instanceof StorageException
) {
1495 requestRebuild(indexId
);
1503 FileContent fileContent
= null;
1504 for (ID
<?
, ?
> indexId
: myNotRequiringContentIndices
) {
1505 if (shouldIndexFile(file
, indexId
)) {
1508 if (fileContent
== null) {
1509 fileContent
= new FileContent(file
);
1511 updateSingleIndex(indexId
, file
, fileContent
);
1513 catch (StorageException e
) {
1515 requestRebuild(indexId
);
1519 IndexingStamp
.flushCache();
1521 if (oldStuff
&& file
instanceof NewVirtualFile
) {
1522 ((NewVirtualFile
)file
).setFlag(ALREADY_PROCESSED
, true);
1527 if (myProgressIndicator
!= null) {
1528 myProgressIndicator
.setText("Scanning files to index");
1529 myProgressIndicator
.setText2(file
.getPresentableUrl());
1536 private boolean shouldUpdateIndex(final VirtualFile file
, final ID
<?
, ?
> indexId
) {
1537 return getInputFilter(indexId
).acceptInput(file
) &&
1538 (isMock(file
) || IndexingStamp
.isFileIndexed(file
, indexId
, IndexInfrastructure
.getIndexCreationStamp(indexId
)));
1541 private boolean shouldIndexFile(final VirtualFile file
, final ID
<?
, ?
> indexId
) {
1542 return getInputFilter(indexId
).acceptInput(file
) &&
1543 (isMock(file
) || !IndexingStamp
.isFileIndexed(file
, indexId
, IndexInfrastructure
.getIndexCreationStamp(indexId
)));
1546 private boolean isUnderConfigOrSystem(VirtualFile file
) {
1547 final String filePath
= file
.getPath();
1548 if (myConfigPath
!= null && FileUtil
.startsWith(filePath
, myConfigPath
)) {
1551 if (mySystemPath
!= null && FileUtil
.startsWith(filePath
, mySystemPath
)) {
1557 private static boolean isMock(final VirtualFile file
) {
1558 return !(file
instanceof NewVirtualFile
);
1561 private boolean isTooLarge(VirtualFile file
) {
1562 if (SingleRootFileViewProvider
.isTooLarge(file
)) {
1563 final FileType type
= FileTypeManager
.getInstance().getFileTypeByFile(file
);
1564 return !myNoLimitCheckTypes
.contains(type
);
1569 public CollectingContentIterator
createContentIterator() {
1570 return new UnindexedFilesFinder();
1573 public void registerIndexableSet(IndexableFileSet set
) {
1574 myIndexableSets
.add(set
);
1577 public void removeIndexableSet(IndexableFileSet set
) {
1578 myChangedFilesUpdater
.forceUpdate();
1579 myIndexableSets
.remove(set
);
1583 private static PsiFile
findLatestKnownPsiForUncomittedDocument(Document doc
, Project project
) {
1584 return PsiDocumentManager
.getInstance(project
).getCachedPsiFile(doc
);
1587 private static class IndexableFilesFilter
implements InputFilter
{
1588 private final InputFilter myDelegate
;
1590 private IndexableFilesFilter(InputFilter delegate
) {
1591 myDelegate
= delegate
;
1594 public boolean acceptInput(final VirtualFile file
) {
1595 return file
instanceof VirtualFileWithId
&& myDelegate
.acceptInput(file
);
1599 private static void cleanupProcessedFlag() {
1600 final VirtualFile
[] roots
= ManagingFS
.getInstance().getRoots();
1601 for (VirtualFile root
: roots
) {
1602 cleanProcessedFlag(root
);
1606 private static void cleanProcessedFlag(final VirtualFile file
) {
1607 if (!(file
instanceof NewVirtualFile
)) return;
1609 final NewVirtualFile nvf
= (NewVirtualFile
)file
;
1610 if (file
.isDirectory()) {
1611 for (VirtualFile child
: nvf
.getCachedChildren()) {
1612 cleanProcessedFlag(child
);
1616 /* nvf.clearCachedFileType(); */
1617 nvf
.setFlag(ALREADY_PROCESSED
, false);
1621 private static class StorageGuard
{
1622 private int myHolds
= 0;
1624 public interface Holder
{
1628 private final Holder myTrueHolder
= new Holder() {
1629 public void leave() {
1630 StorageGuard
.this.leave(true);
1633 private final Holder myFalseHolder
= new Holder() {
1634 public void leave() {
1635 StorageGuard
.this.leave(false);
1639 public synchronized Holder
enter(boolean mode
) {
1641 while (myHolds
< 0) {
1645 catch (InterruptedException ignored
) {
1649 return myTrueHolder
;
1652 while (myHolds
> 0) {
1656 catch (InterruptedException ignored
) {
1660 return myFalseHolder
;
1664 private synchronized void leave(boolean mode
) {
1665 myHolds
+= (mode?
-1 : 1);