1 package com
.intellij
.util
.indexing
;
3 import com
.intellij
.AppTopics
;
4 import com
.intellij
.ide
.startup
.CacheUpdater
;
5 import com
.intellij
.ide
.startup
.FileSystemSynchronizer
;
6 import com
.intellij
.lang
.ASTNode
;
7 import com
.intellij
.openapi
.application
.*;
8 import com
.intellij
.openapi
.components
.ApplicationComponent
;
9 import com
.intellij
.openapi
.diagnostic
.Logger
;
10 import com
.intellij
.openapi
.editor
.Document
;
11 import com
.intellij
.openapi
.editor
.highlighter
.EditorHighlighter
;
12 import com
.intellij
.openapi
.editor
.impl
.DocumentImpl
;
13 import com
.intellij
.openapi
.extensions
.Extensions
;
14 import com
.intellij
.openapi
.fileEditor
.FileDocumentManager
;
15 import com
.intellij
.openapi
.fileEditor
.impl
.LoadTextUtil
;
16 import com
.intellij
.openapi
.fileTypes
.FileTypeEvent
;
17 import com
.intellij
.openapi
.fileTypes
.FileTypeListener
;
18 import com
.intellij
.openapi
.progress
.*;
19 import com
.intellij
.openapi
.project
.Project
;
20 import com
.intellij
.openapi
.project
.ProjectManager
;
21 import com
.intellij
.openapi
.roots
.CollectingContentIterator
;
22 import com
.intellij
.openapi
.roots
.ContentIterator
;
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
.util
.Pair
;
27 import com
.intellij
.openapi
.util
.io
.FileUtil
;
28 import com
.intellij
.openapi
.vfs
.*;
29 import com
.intellij
.openapi
.vfs
.ex
.VirtualFileManagerEx
;
30 import com
.intellij
.openapi
.vfs
.impl
.BulkVirtualFileListenerAdapter
;
31 import com
.intellij
.openapi
.vfs
.newvfs
.ManagingFS
;
32 import com
.intellij
.openapi
.vfs
.newvfs
.NewVirtualFile
;
33 import com
.intellij
.openapi
.vfs
.newvfs
.events
.VFileEvent
;
34 import com
.intellij
.openapi
.vfs
.newvfs
.persistent
.PersistentFS
;
35 import com
.intellij
.psi
.*;
36 import com
.intellij
.psi
.impl
.PsiDocumentTransactionListener
;
37 import com
.intellij
.psi
.impl
.source
.PsiFileImpl
;
38 import com
.intellij
.psi
.stubs
.StubUpdatingIndex
;
39 import com
.intellij
.util
.ArrayUtil
;
40 import com
.intellij
.util
.CommonProcessors
;
41 import com
.intellij
.util
.Processor
;
42 import com
.intellij
.util
.concurrency
.Semaphore
;
43 import com
.intellij
.util
.containers
.ConcurrentHashSet
;
44 import com
.intellij
.util
.io
.IOUtil
;
45 import com
.intellij
.util
.messages
.MessageBus
;
46 import com
.intellij
.util
.messages
.MessageBusConnection
;
47 import gnu
.trove
.TObjectIntHashMap
;
48 import org
.jetbrains
.annotations
.NonNls
;
49 import org
.jetbrains
.annotations
.NotNull
;
50 import org
.jetbrains
.annotations
.Nullable
;
55 import java
.util
.concurrent
.CopyOnWriteArrayList
;
56 import java
.util
.concurrent
.atomic
.AtomicInteger
;
57 import java
.util
.concurrent
.locks
.Lock
;
60 * @author Eugene Zhuravlev
64 public class FileBasedIndex
implements ApplicationComponent
{
65 private static final Logger LOG
= Logger
.getInstance("#com.intellij.util.indexing.FileBasedIndex");
67 private static final String CORRUPTION_MARKER_NAME
= "corruption.marker";
68 private final Map
<ID
<?
, ?
>, Pair
<UpdatableIndex
<?
, ?
, FileContent
>, InputFilter
>> myIndices
= new HashMap
<ID
<?
, ?
>, Pair
<UpdatableIndex
<?
, ?
, FileContent
>, InputFilter
>>();
69 private final Map
<ID
<?
, ?
>, Semaphore
> myUnsavedDataIndexingSemaphores
= new HashMap
<ID
<?
,?
>, Semaphore
>();
70 private final TObjectIntHashMap
<ID
<?
, ?
>> myIndexIdToVersionMap
= new TObjectIntHashMap
<ID
<?
, ?
>>();
71 private final Set
<ID
<?
, ?
>> myNeedContentLoading
= new HashSet
<ID
<?
, ?
>>();
73 private final PerIndexDocumentMap
<Long
> myLastIndexedDocStamps
= new PerIndexDocumentMap
<Long
>() {
75 protected Long
createDefault(Document document
) {
79 private final PerIndexDocumentMap
<CharSequence
> myLastIndexedUnsavedContent
= new PerIndexDocumentMap
<CharSequence
>() {
81 protected CharSequence
createDefault(Document document
) {
82 return loadContent(FileDocumentManager
.getInstance().getFile(document
));
86 private final ChangedFilesUpdater myChangedFilesUpdater
;
88 private final List
<IndexableFileSet
> myIndexableSets
= new CopyOnWriteArrayList
<IndexableFileSet
>();
90 public static final int OK
= 1;
91 public static final int REQUIRES_REBUILD
= 2;
92 public static final int REBUILD_IN_PROGRESS
= 3;
93 private final Map
<ID
<?
, ?
>, AtomicInteger
> myRebuildStatus
= new HashMap
<ID
<?
,?
>, AtomicInteger
>();
95 private final VirtualFileManagerEx myVfManager
;
96 private final ConcurrentHashSet
<ID
<?
, ?
>> myUpToDateIndices
= new ConcurrentHashSet
<ID
<?
, ?
>>();
97 private final Map
<Document
, PsiFile
> myTransactionMap
= new HashMap
<Document
, PsiFile
>();
99 private final FileContentStorage myFileContentAttic
;
100 private final Map
<ID
<?
, ?
>, FileBasedIndexExtension
<?
, ?
>> myExtentions
= new HashMap
<ID
<?
,?
>, FileBasedIndexExtension
<?
,?
>>();
102 private static final int ALREADY_PROCESSED
= 0x02;
104 public void requestReindex(final VirtualFile file
) {
105 myChangedFilesUpdater
.scheduleInvalidation(file
, true);
108 public interface InputFilter
{
109 boolean acceptInput(VirtualFile file
);
112 public FileBasedIndex(final VirtualFileManagerEx vfManager
, MessageBus bus
) throws IOException
{
113 myVfManager
= vfManager
;
115 final MessageBusConnection connection
= bus
.connect();
116 connection
.subscribe(PsiDocumentTransactionListener
.TOPIC
, new PsiDocumentTransactionListener() {
117 public void transactionStarted(final Document doc
, final PsiFile file
) {
119 myTransactionMap
.put(doc
, file
);
120 myUpToDateIndices
.clear();
124 public void transactionCompleted(final Document doc
, final PsiFile file
) {
125 myTransactionMap
.remove(doc
);
129 connection
.subscribe(AppTopics
.FILE_TYPES
, new FileTypeListener() {
130 public void beforeFileTypesChanged(final FileTypeEvent event
) {
131 cleanupProcessedFlag();
132 // TODO: temporary solution for tests to avoid 'twin stubs' problem
133 // Correct index update will require more information from FileType events
134 if (ApplicationManager
.getApplication().isUnitTestMode()) {
135 requestRebuild(StubUpdatingIndex
.INDEX_ID
);
139 public void fileTypesChanged(final FileTypeEvent event
) {
143 ApplicationManager
.getApplication().addApplicationListener(new ApplicationAdapter() {
144 public void writeActionStarted(Object action
) {
145 myUpToDateIndices
.clear();
149 final File workInProgressFile
= getMarkerFile();
150 if (workInProgressFile
.exists()) {
151 // previous IDEA session was closed incorrectly, so drop all indices
152 FileUtil
.delete(PathManager
.getIndexRoot());
156 final FileBasedIndexExtension
[] extensions
= Extensions
.getExtensions(FileBasedIndexExtension
.EXTENSION_POINT_NAME
);
157 for (FileBasedIndexExtension
<?
, ?
> extension
: extensions
) {
158 myRebuildStatus
.put(extension
.getName(), new AtomicInteger(OK
));
161 final File corruptionMarker
= new File(PathManager
.getIndexRoot(), CORRUPTION_MARKER_NAME
);
162 final boolean currentVersionCorrupted
= corruptionMarker
.exists();
163 for (FileBasedIndexExtension
<?
, ?
> extension
: extensions
) {
164 registerIndexer(extension
, currentVersionCorrupted
);
166 FileUtil
.delete(corruptionMarker
);
167 dropUnregisteredIndices();
169 // check if rebuild was requested for any index during registration
170 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
171 if (myRebuildStatus
.get(indexId
).compareAndSet(REQUIRES_REBUILD
, OK
)) {
175 catch (StorageException e
) {
176 requestRebuild(indexId
);
182 myChangedFilesUpdater
= new ChangedFilesUpdater();
183 //vfManager.addVirtualFileListener(myChangedFilesUpdater);
184 connection
.subscribe(VirtualFileManager
.VFS_CHANGES
, new BulkVirtualFileListenerAdapter(myChangedFilesUpdater
) {
185 public void before(final List
<?
extends VFileEvent
> events
) {
186 if (ApplicationManager
.getApplication().isDispatchThread()) {
187 new Task
.Modal(null, "Preparing index invalidation...", false) {
188 public void run(@NotNull ProgressIndicator indicator
) {
189 indicator
.setIndeterminate(true);
190 deliverBeforeEvents(events
);
195 deliverBeforeEvents(events
);
199 private void deliverBeforeEvents(List
<?
extends VFileEvent
> events
) {
200 super.before(events
);
204 vfManager
.registerRefreshUpdater(myChangedFilesUpdater
);
205 myFileContentAttic
= new FileContentStorage(new File(PathManager
.getIndexRoot(), "updates.tmp"));
208 workInProgressFile
.createNewFile();
209 saveRegisteredInices(myIndices
.keySet());
213 private static FileBasedIndex ourInstance
= CachedSingletonsRegistry
.markCachedField(FileBasedIndex
.class);
214 public static FileBasedIndex
getInstance() {
215 if (ourInstance
== null) {
216 ourInstance
= ApplicationManager
.getApplication().getComponent(FileBasedIndex
.class);
223 * @return true if registered index requires full rebuild for some reason, e.g. is just created or corrupted @param extension
224 * @param isCurrentVersionCorrupted
226 private <K
, V
> void registerIndexer(final FileBasedIndexExtension
<K
, V
> extension
, final boolean isCurrentVersionCorrupted
) throws IOException
{
227 final ID
<K
, V
> name
= extension
.getName();
228 final int version
= extension
.getVersion();
229 if (extension
.dependsOnFileContent()) {
230 myNeedContentLoading
.add(name
);
232 myIndexIdToVersionMap
.put(name
, version
);
233 final File versionFile
= IndexInfrastructure
.getVersionFile(name
);
234 if (isCurrentVersionCorrupted
|| IndexInfrastructure
.versionDiffers(versionFile
, version
)) {
235 FileUtil
.delete(IndexInfrastructure
.getIndexRootDir(name
));
236 IndexInfrastructure
.rewriteVersion(versionFile
, version
);
239 for (int attempt
= 0; attempt
< 2; attempt
++) {
241 final MapIndexStorage
<K
, V
> storage
= new MapIndexStorage
<K
, V
>(IndexInfrastructure
.getStorageFile(name
), extension
.getKeyDescriptor(), extension
.getValueExternalizer(), extension
.getCacheSize());
242 final IndexStorage
<K
, V
> memStorage
= new MemoryIndexStorage
<K
, V
>(storage
);
243 final UpdatableIndex
<K
, V
, FileContent
> index
= createIndex(name
, extension
, memStorage
);
244 myIndices
.put(name
, new Pair
<UpdatableIndex
<?
,?
, FileContent
>, InputFilter
>(index
, new IndexableFilesFilter(extension
.getInputFilter())));
245 myUnsavedDataIndexingSemaphores
.put(name
, new Semaphore());
246 myExtentions
.put(name
, extension
);
249 catch (IOException e
) {
250 FileUtil
.delete(IndexInfrastructure
.getIndexRootDir(name
));
251 IndexInfrastructure
.rewriteVersion(versionFile
, version
);
256 private static void saveRegisteredInices(Collection
<ID
<?
, ?
>> ids
) {
257 final File file
= getRegisteredIndicesFile();
259 file
.getParentFile().mkdirs();
260 file
.createNewFile();
261 final DataOutputStream os
= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file
)));
263 os
.writeInt(ids
.size());
264 for (ID
<?
, ?
> id
: ids
) {
265 IOUtil
.writeString(id
.toString(), os
);
272 catch (IOException ignored
) {
276 private static Set
<String
> readRegistsredIndexNames() {
277 final Set
<String
> result
= new HashSet
<String
>();
279 final DataInputStream in
= new DataInputStream(new BufferedInputStream(new FileInputStream(getRegisteredIndicesFile())));
281 final int size
= in
.readInt();
282 for (int idx
= 0; idx
< size
; idx
++) {
283 result
.add(IOUtil
.readString(in
));
290 catch (IOException ignored
) {
295 private static File
getRegisteredIndicesFile() {
296 return new File(PathManager
.getIndexRoot(), "registered");
299 private static File
getMarkerFile() {
300 return new File(PathManager
.getIndexRoot(), "work_in_progress");
303 private <K
, V
> UpdatableIndex
<K
, V
, FileContent
> createIndex(final ID
<K
, V
> indexId
, final FileBasedIndexExtension
<K
, V
> extension
, final IndexStorage
<K
, V
> storage
) {
304 if (extension
instanceof CustomImplementationFileBasedIndexExtension
) {
305 return ((CustomImplementationFileBasedIndexExtension
<K
, V
, FileContent
>)extension
).createIndexImplementation(indexId
, this, storage
);
307 return new MapReduceIndex
<K
, V
, FileContent
>(indexId
, extension
.getIndexer(), storage
);
312 public String
getComponentName() {
313 return "FileBasedIndex";
316 public void initComponent() {
319 public void disposeComponent() {
320 myChangedFilesUpdater
.forceUpdate();
322 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
323 final UpdatableIndex
<?
, ?
, FileContent
> index
= getIndex(indexId
);
324 assert index
!= null;
328 //myVfManager.removeVirtualFileListener(myChangedFilesUpdater);
329 myVfManager
.unregisterRefreshUpdater(myChangedFilesUpdater
);
331 FileUtil
.delete(getMarkerFile());
334 public void flushCaches() {
335 ApplicationManager
.getApplication().executeOnPooledThread(new Runnable() {
337 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
338 //noinspection ConstantConditions
340 getIndex(indexId
).flush();
342 catch (StorageException e
) {
344 requestRebuild(indexId
);
352 public <K
> Collection
<K
> getAllKeys(final ID
<K
, ?
> indexId
) {
353 Set
<K
> allKeys
= new HashSet
<K
>();
354 processAllKeys(indexId
, new CommonProcessors
.CollectProcessor
<K
>(allKeys
));
358 public <K
> boolean processAllKeys(final ID
<K
, ?
> indexId
, Processor
<K
> processor
) {
360 ensureUpToDate(indexId
);
361 final UpdatableIndex
<K
, ?
, FileContent
> index
= getIndex(indexId
);
362 if (index
== null) return true;
363 return index
.processAllKeys(processor
);
365 catch (StorageException e
) {
366 scheduleRebuild(indexId
, e
);
368 catch (RuntimeException e
) {
369 final Throwable cause
= e
.getCause();
370 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
371 scheduleRebuild(indexId
, cause
);
381 private static final ThreadLocal
<Integer
> myUpToDateCheckState
= new ThreadLocal
<Integer
>();
383 public void disableUpToDateCheckForCurrentThread() {
384 final Integer currentValue
= myUpToDateCheckState
.get();
385 myUpToDateCheckState
.set(currentValue
== null?
1 : currentValue
.intValue() + 1);
388 public void enableUpToDateCheckForCurrentThread() {
389 final Integer currentValue
= myUpToDateCheckState
.get();
390 if (currentValue
!= null) {
391 final int newValue
= currentValue
.intValue() - 1;
393 myUpToDateCheckState
.set(newValue
);
396 myUpToDateCheckState
.remove();
401 private boolean isUpToDateCheckEnabled() {
402 final Integer value
= myUpToDateCheckState
.get();
403 return value
== null || value
.intValue() == 0;
407 private final ThreadLocal
<Boolean
> myReentrancyGuard
= new ThreadLocal
<Boolean
>() {
408 protected Boolean
initialValue() {
409 return Boolean
.FALSE
;
414 * DO NOT CALL DIRECTLY IN CLIENT CODE
415 * The method is internal to indexing engine end is called internally. The method is public due to implementation details
417 public <K
> void ensureUpToDate(final ID
<K
, ?
> indexId
) {
418 if (myReentrancyGuard
.get().booleanValue()) {
419 //assert false : "ensureUpToDate() is not reentrant!";
422 myReentrancyGuard
.set(Boolean
.TRUE
);
425 myChangedFilesUpdater
.ensureAllInvalidateTasksCompleted();
427 if (isUpToDateCheckEnabled()) {
429 checkRebuild(indexId
);
430 indexUnsavedDocuments(indexId
);
432 catch (StorageException e
) {
433 scheduleRebuild(indexId
, e
);
435 catch (RuntimeException e
) {
436 final Throwable cause
= e
.getCause();
437 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
438 scheduleRebuild(indexId
, e
);
447 myReentrancyGuard
.set(Boolean
.FALSE
);
452 public <K
, V
> List
<V
> getValues(final ID
<K
, V
> indexId
, @NotNull K dataKey
, final VirtualFileFilter filter
) {
453 final List
<V
> values
= new ArrayList
<V
>();
454 processValuesImpl(indexId
, dataKey
, true, null, new ValueProcessor
<V
>() {
455 public boolean process(final VirtualFile file
, final V value
) {
464 public <K
, V
> Collection
<VirtualFile
> getContainingFiles(final ID
<K
, V
> indexId
, @NotNull K dataKey
, final VirtualFileFilter filter
) {
465 final Set
<VirtualFile
> files
= new HashSet
<VirtualFile
>();
466 processValuesImpl(indexId
, dataKey
, false, null, new ValueProcessor
<V
>() {
467 public boolean process(final VirtualFile file
, final V value
) {
476 public interface ValueProcessor
<V
> {
478 * @param value a value to process
479 * @param file the file the value came from
480 * @return false if no further processing is needed, true otherwise
482 boolean process(VirtualFile file
, V value
);
486 * @return false if ValueProcessor.process() returned false; true otherwise or if ValueProcessor was not called at all
488 public <K
, V
> boolean processValues(final ID
<K
, V
> indexId
, final @NotNull K dataKey
, @Nullable final VirtualFile inFile
,
489 ValueProcessor
<V
> processor
, final VirtualFileFilter filter
) {
490 return processValuesImpl(indexId
, dataKey
, false, inFile
, processor
, filter
);
493 private <K
, V
> boolean processValuesImpl(final ID
<K
, V
> indexId
, final K dataKey
, boolean ensureValueProcessedOnce
,
494 @Nullable final VirtualFile restrictToFile
, ValueProcessor
<V
> processor
,
495 final VirtualFileFilter filter
) {
497 ensureUpToDate(indexId
);
498 final UpdatableIndex
<K
, V
, FileContent
> index
= getIndex(indexId
);
503 final Lock readLock
= index
.getReadLock();
506 final ValueContainer
<V
> container
= index
.getData(dataKey
);
508 boolean shouldContinue
= true;
510 if (restrictToFile
!= null) {
511 if (restrictToFile
instanceof VirtualFileWithId
) {
512 final int restrictedFileId
= getFileId(restrictToFile
);
513 for (final Iterator
<V
> valueIt
= container
.getValueIterator(); valueIt
.hasNext();) {
514 final V value
= valueIt
.next();
515 if (container
.isAssociated(value
, restrictedFileId
)) {
516 shouldContinue
= processor
.process(restrictToFile
, value
);
517 if (!shouldContinue
) {
525 final PersistentFS fs
= (PersistentFS
)ManagingFS
.getInstance();
526 VALUES_LOOP
: for (final Iterator
<V
> valueIt
= container
.getValueIterator(); valueIt
.hasNext();) {
527 final V value
= valueIt
.next();
528 for (final ValueContainer
.IntIterator inputIdsIterator
= container
.getInputIdsIterator(value
); inputIdsIterator
.hasNext();) {
529 final int id
= inputIdsIterator
.next();
530 VirtualFile file
= IndexInfrastructure
.findFileById(fs
, id
);
531 if (file
!= null && filter
.accept(file
)) {
532 shouldContinue
= processor
.process(file
, value
);
533 if (!shouldContinue
) {
536 if (ensureValueProcessedOnce
) {
537 break; // continue with the next value
543 return shouldContinue
;
546 index
.getReadLock().unlock();
549 catch (StorageException e
) {
550 scheduleRebuild(indexId
, e
);
552 catch (RuntimeException e
) {
553 final Throwable cause
= e
.getCause();
554 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
555 scheduleRebuild(indexId
, cause
);
564 public interface AllValuesProcessor
<V
> {
565 void process(final int inputId
, V value
);
568 public <K
, V
> void processAllValues(final ID
<K
, V
> indexId
, AllValuesProcessor
<V
> processor
) {
570 ensureUpToDate(indexId
);
571 final UpdatableIndex
<K
, V
, FileContent
> index
= getIndex(indexId
);
576 index
.getReadLock().lock();
577 for (K dataKey
: index
.getAllKeys()) {
578 final ValueContainer
<V
> container
= index
.getData(dataKey
);
579 for (final Iterator
<V
> it
= container
.getValueIterator(); it
.hasNext();) {
580 final V value
= it
.next();
581 for (final ValueContainer
.IntIterator inputsIt
= container
.getInputIdsIterator(value
); inputsIt
.hasNext();) {
582 processor
.process(inputsIt
.next(), value
);
588 index
.getReadLock().unlock();
591 catch (StorageException e
) {
592 scheduleRebuild(indexId
, e
);
594 catch (RuntimeException e
) {
595 final Throwable cause
= e
.getCause();
596 if (cause
instanceof StorageException
|| cause
instanceof IOException
) {
597 scheduleRebuild(indexId
, e
);
605 private <K
> void scheduleRebuild(final ID
<K
, ?
> indexId
, final Throwable e
) {
606 requestRebuild(indexId
);
608 checkRebuild(indexId
);
611 private void checkRebuild(final ID
<?
, ?
> indexId
) {
612 if (myRebuildStatus
.get(indexId
).compareAndSet(REQUIRES_REBUILD
, REBUILD_IN_PROGRESS
)) {
613 cleanupProcessedFlag();
615 final Runnable rebuildRunnable
= new Runnable() {
618 final FileSystemSynchronizer synchronizer
= new FileSystemSynchronizer();
619 synchronizer
.setCancelable(false);
620 for (Project project
: ProjectManager
.getInstance().getOpenProjects()) {
621 synchronizer
.registerCacheUpdater(new UnindexedFilesUpdater(project
, ProjectRootManager
.getInstance(project
), FileBasedIndex
.this));
626 synchronizer
.execute();
628 catch (StorageException e
) {
629 requestRebuild(indexId
);
633 myRebuildStatus
.get(indexId
).compareAndSet(REBUILD_IN_PROGRESS
, OK
);
638 final Application application
= ApplicationManager
.getApplication();
639 if (application
.isUnitTestMode()) {
640 rebuildRunnable
.run();
643 SwingUtilities
.invokeLater(new Runnable() {
645 new Task
.Modal(null, "Updating index", false) {
646 public void run(@NotNull final ProgressIndicator indicator
) {
647 indicator
.setIndeterminate(true);
648 rebuildRunnable
.run();
656 if (myRebuildStatus
.get(indexId
).get() == REBUILD_IN_PROGRESS
) {
657 throw new ProcessCanceledException();
661 private void clearIndex(final ID
<?
, ?
> indexId
) throws StorageException
{
662 final UpdatableIndex
<?
, ?
, FileContent
> index
= getIndex(indexId
);
663 assert index
!= null;
666 IndexInfrastructure
.rewriteVersion(IndexInfrastructure
.getVersionFile(indexId
), myIndexIdToVersionMap
.get(indexId
));
668 catch (IOException e
) {
673 private Set
<Document
> getUnsavedOrTransactedDocuments() {
674 Set
<Document
> docs
= new HashSet
<Document
>(Arrays
.asList(FileDocumentManager
.getInstance().getUnsavedDocuments()));
675 docs
.addAll(myTransactionMap
.keySet());
679 private void indexUnsavedDocuments(ID
<?
, ?
> indexId
) throws StorageException
{
680 myChangedFilesUpdater
.forceUpdate();
682 if (myUpToDateIndices
.contains(indexId
)) {
683 return; // no need to index unsaved docs
686 final Set
<Document
> documents
= getUnsavedOrTransactedDocuments();
687 if (!documents
.isEmpty()) {
688 // now index unsaved data
689 final StorageGuard
.Holder guard
= setDataBufferingEnabled(true);
691 final Semaphore semaphore
= myUnsavedDataIndexingSemaphores
.get(indexId
);
694 for (Document document
: documents
) {
695 indexUnsavedDocument(document
, indexId
);
701 while (!semaphore
.waitFor(500)) { // may need to wait until another thread is done with indexing
702 if (Thread
.holdsLock(PsiLock
.LOCK
)) {
703 break; // hack. Most probably that other indexing threads is waiting for PsiLock, which we're are holding.
706 myUpToDateIndices
.add(indexId
); // safe to set the flag here, becase it will be cleared under the WriteAction
715 private interface DocumentContent
{
717 long getModificationStamp();
720 private static class AuthenticContent
implements DocumentContent
{
721 private final Document myDocument
;
723 private AuthenticContent(final Document document
) {
724 myDocument
= document
;
727 public String
getText() {
728 return myDocument
.getText();
731 public long getModificationStamp() {
732 return myDocument
.getModificationStamp();
736 private static class PsiContent
implements DocumentContent
{
737 private final Document myDocument
;
738 private final PsiFile myFile
;
740 private PsiContent(final Document document
, final PsiFile file
) {
741 myDocument
= document
;
745 public String
getText() {
746 if (myFile
.getModificationStamp() != myDocument
.getModificationStamp()) {
747 final ASTNode node
= myFile
.getNode();
749 return node
.getText();
751 return myDocument
.getText();
754 public long getModificationStamp() {
755 return myFile
.getModificationStamp();
759 private void indexUnsavedDocument(final Document document
, final ID
<?
, ?
> requestedIndexId
) throws StorageException
{
760 final VirtualFile vFile
= FileDocumentManager
.getInstance().getFile(document
);
761 if (!(vFile
instanceof VirtualFileWithId
) || !vFile
.isValid()) {
765 PsiFile dominantContentFile
= findDominantPsiForDocument(document
);
767 DocumentContent content
;
768 if (dominantContentFile
!= null && dominantContentFile
.getModificationStamp() != document
.getModificationStamp()) {
769 content
= new PsiContent(document
, dominantContentFile
);
772 content
= new AuthenticContent(document
);
775 final long currentDocStamp
= content
.getModificationStamp();
776 if (currentDocStamp
!= myLastIndexedDocStamps
.getAndSet(document
, requestedIndexId
, currentDocStamp
).longValue()) {
777 final FileContent newFc
= new FileContent(vFile
, content
.getText(), vFile
.getCharset());
779 if (dominantContentFile
!= null) {
780 dominantContentFile
.putUserData(PsiFileImpl
.BUILDING_STUB
, true);
781 newFc
.putUserData(PSI_FILE
, dominantContentFile
);
784 if (content
instanceof AuthenticContent
) {
785 newFc
.putUserData(EDITOR_HIGHLIGHTER
, document
instanceof DocumentImpl
786 ?
((DocumentImpl
)document
).getEditorHighlighterForCachesBuilding() : null);
789 CharSequence lastIndexed
= myLastIndexedUnsavedContent
.get(document
, requestedIndexId
);
790 final FileContent oldFc
= new FileContent(vFile
, lastIndexed
, vFile
.getCharset());
791 if (getInputFilter(requestedIndexId
).acceptInput(vFile
)) {
792 final int inputId
= Math
.abs(getFileId(vFile
));
793 getIndex(requestedIndexId
).update(inputId
, newFc
, oldFc
);
796 if (dominantContentFile
!= null) {
797 dominantContentFile
.putUserData(PsiFileImpl
.BUILDING_STUB
, null);
800 myLastIndexedUnsavedContent
.put(document
, requestedIndexId
, newFc
.getContentAsText());
804 public static final Key
<PsiFile
> PSI_FILE
= new Key
<PsiFile
>("PSI for stubs");
805 public static final Key
<EditorHighlighter
> EDITOR_HIGHLIGHTER
= new Key
<EditorHighlighter
>("Editor");
806 public static final Key
<Project
> PROJECT
= new Key
<Project
>("Context project");
807 public static final Key
<VirtualFile
> VIRTUAL_FILE
= new Key
<VirtualFile
>("Context virtual file");
810 private PsiFile
findDominantPsiForDocument(final Document document
) {
811 if (myTransactionMap
.containsKey(document
)) {
812 return myTransactionMap
.get(document
);
815 return findLatestKnownPsiForUncomittedDocument(document
);
818 private final StorageGuard myStorageLock
= new StorageGuard();
820 private StorageGuard
.Holder
setDataBufferingEnabled(final boolean enabled
) {
821 final StorageGuard
.Holder holder
= myStorageLock
.enter(enabled
);
823 synchronized (myLastIndexedDocStamps
) {
824 myLastIndexedDocStamps
.clear();
825 myLastIndexedUnsavedContent
.clear();
828 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
829 final MapReduceIndex index
= (MapReduceIndex
)getIndex(indexId
);
830 assert index
!= null;
831 final IndexStorage indexStorage
= index
.getStorage();
832 ((MemoryIndexStorage
)indexStorage
).setBufferingEnabled(enabled
);
837 private void dropUnregisteredIndices() {
838 final Set
<String
> indicesToDrop
= readRegistsredIndexNames();
839 for (ID
<?
, ?
> key
: myIndices
.keySet()) {
840 indicesToDrop
.remove(key
.toString());
842 for (String s
: indicesToDrop
) {
843 FileUtil
.delete(IndexInfrastructure
.getIndexRootDir(ID
.create(s
)));
847 public void requestRebuild(ID
<?
, ?
> indexId
) {
848 cleanupProcessedFlag();
849 LOG
.info("Rebuild requested for index " + indexId
, new Throwable());
850 myRebuildStatus
.get(indexId
).set(REQUIRES_REBUILD
);
853 private <K
, V
> UpdatableIndex
<K
, V
, FileContent
> getIndex(ID
<K
, V
> indexId
) {
854 final Pair
<UpdatableIndex
<?
, ?
, FileContent
>, InputFilter
> pair
= myIndices
.get(indexId
);
855 //noinspection unchecked
856 return pair
!= null?
(UpdatableIndex
<K
,V
, FileContent
>)pair
.getFirst() : null;
859 private InputFilter
getInputFilter(ID
<?
, ?
> indexId
) {
860 final Pair
<UpdatableIndex
<?
, ?
, FileContent
>, InputFilter
> pair
= myIndices
.get(indexId
);
861 return pair
!= null? pair
.getSecond() : null;
864 public void indexFileContent(com
.intellij
.ide
.startup
.FileContent content
) {
865 myChangedFilesUpdater
.ensureAllInvalidateTasksCompleted();
866 final VirtualFile file
= content
.getVirtualFile();
867 FileContent fc
= null;
868 FileContent oldContent
= null;
869 final byte[] oldBytes
= myFileContentAttic
.remove(file
);
870 final boolean forceIndexing
= oldBytes
!= null;
872 PsiFile psiFile
= null;
873 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
874 if (forceIndexing ?
getInputFilter(indexId
).acceptInput(file
) : shouldIndexFile(file
, indexId
)) {
878 currentBytes
= content
.getBytes();
880 catch (IOException e
) {
881 currentBytes
= ArrayUtil
.EMPTY_BYTE_ARRAY
;
883 fc
= new FileContent(file
, currentBytes
);
885 psiFile
= content
.getUserData(PSI_FILE
);
886 if (psiFile
!= null) {
887 psiFile
.putUserData(PsiFileImpl
.BUILDING_STUB
, true);
888 fc
.putUserData(PSI_FILE
, psiFile
);
890 Project project
= content
.getUserData(PROJECT
);
891 fc
.putUserData(PROJECT
, project
);
892 oldContent
= oldBytes
!= null?
new FileContent(file
, oldBytes
) : null;
896 updateSingleIndex(indexId
, file
, fc
, oldContent
);
898 catch (StorageException e
) {
899 requestRebuild(indexId
);
905 if (psiFile
!= null) {
906 psiFile
.putUserData(PsiFileImpl
.BUILDING_STUB
, null);
909 IndexingStamp
.flushCache();
912 private void updateSingleIndex(final ID
<?
, ?
> indexId
, final VirtualFile file
, final FileContent currentFC
, final FileContent oldFC
)
913 throws StorageException
{
915 final StorageGuard
.Holder lock
= setDataBufferingEnabled(false);
918 final int inputId
= Math
.abs(getFileId(file
));
920 final UpdatableIndex
<?
, ?
, FileContent
> index
= getIndex(indexId
);
921 assert index
!= null;
923 index
.update(inputId
, currentFC
, oldFC
);
924 ApplicationManager
.getApplication().runReadAction(new Runnable() {
926 if (file
.isValid()) {
927 if (currentFC
!= null) {
928 IndexingStamp
.update(file
, indexId
, IndexInfrastructure
.getIndexCreationStamp(indexId
));
931 // mark the file as unindexed
932 IndexingStamp
.update(file
, indexId
, -1L);
943 public static int getFileId(final VirtualFile file
) {
944 if (file
instanceof VirtualFileWithId
) {
945 return ((VirtualFileWithId
)file
).getId();
948 throw new IllegalArgumentException("Virtual file doesn't support id: " + file
+ ", implementation class: " + file
.getClass().getName());
951 private static CharSequence
loadContent(VirtualFile file
) {
952 return LoadTextUtil
.loadText(file
, true);
955 private abstract static class InvalidationTask
implements Runnable
{
956 private final VirtualFile mySubj
;
958 protected InvalidationTask(final VirtualFile subj
) {
962 public VirtualFile
getSubj() {
967 private final class ChangedFilesUpdater
extends VirtualFileAdapter
implements CacheUpdater
{
968 private final Set
<VirtualFile
> myFilesToUpdate
= Collections
.synchronizedSet(new HashSet
<VirtualFile
>());
969 private final Queue
<InvalidationTask
> myFutureInvalidations
= new LinkedList
<InvalidationTask
>();
970 private final ManagingFS myManagingFS
= ManagingFS
.getInstance();
971 // No need to react on movement events since files stay valid, their ids don't change and all associated attributes remain intact.
973 public void fileCreated(final VirtualFileEvent event
) {
977 public void fileDeleted(final VirtualFileEvent event
) {
978 myFilesToUpdate
.remove(event
.getFile()); // no need to update it anymore
981 public void fileCopied(final VirtualFileCopyEvent event
) {
985 public void beforeFileDeletion(final VirtualFileEvent event
) {
986 scheduleInvalidation(event
.getFile(), false);
989 public void beforeContentsChange(final VirtualFileEvent event
) {
990 scheduleInvalidation(event
.getFile(), true);
993 public void contentsChanged(final VirtualFileEvent event
) {
997 public void beforePropertyChange(final VirtualFilePropertyEvent event
) {
998 if (event
.getPropertyName().equals(VirtualFile
.PROP_NAME
)) {
999 // indexes may depend on file name
1000 final VirtualFile file
= event
.getFile();
1001 if (!file
.isDirectory()) {
1002 // name change may lead to filetype change so the file might become not indexable
1003 // in general case have to 'unindex' the file and index it again if needed after the name has been changed
1004 scheduleInvalidation(file
, false);
1009 public void propertyChanged(final VirtualFilePropertyEvent event
) {
1010 if (event
.getPropertyName().equals(VirtualFile
.PROP_NAME
)) {
1011 // indexes may depend on file name
1012 if (!event
.getFile().isDirectory()) {
1018 private void markDirty(final VirtualFileEvent event
) {
1019 cleanProcessedFlag(event
.getFile());
1020 iterateIndexableFiles(event
.getFile(), new Processor
<VirtualFile
>() {
1021 public boolean process(final VirtualFile file
) {
1022 FileContent fileContent
= null;
1023 final boolean isTooLarge
= SingleRootFileViewProvider
.isTooLarge(file
);
1024 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
1025 if (getInputFilter(indexId
).acceptInput(file
)) {
1026 if (myNeedContentLoading
.contains(indexId
)) {
1028 myFilesToUpdate
.add(file
);
1033 if (fileContent
== null) {
1034 fileContent
= new FileContent(file
);
1036 updateSingleIndex(indexId
, file
, fileContent
, null);
1038 catch (StorageException e
) {
1040 requestRebuild(indexId
);
1046 IndexingStamp
.flushCache();
1052 public void scheduleInvalidation(final VirtualFile file
, final boolean saveContent
) {
1053 if (file
.isDirectory()) {
1054 if (isMock(file
) || myManagingFS
.wereChildrenAccessed(file
)) {
1055 for (VirtualFile child
: file
.getChildren()) {
1056 scheduleInvalidation(child
, saveContent
);
1061 cleanProcessedFlag(file
);
1063 final boolean isTooLarge
= SingleRootFileViewProvider
.isTooLarge(file
);
1064 final List
<ID
<?
, ?
>> affectedIndices
= new ArrayList
<ID
<?
, ?
>>(myIndices
.size());
1065 FileContent fileContent
= null;
1067 //final int fileId = getFileId(file);
1068 //LOG.assertTrue(fileId >= 0);
1069 //synchronized (myInvalidationInProgress) {
1070 // myInvalidationInProgress.add(fileId);
1073 for (ID
<?
, ?
> indexId
: myIndices
.keySet()) {
1074 if (shouldUpdateIndex(file
, indexId
)) {
1075 if (myNeedContentLoading
.contains(indexId
)) {
1077 affectedIndices
.add(indexId
);
1081 // invalidate it synchronously
1083 if (fileContent
== null) {
1084 fileContent
= new FileContent(file
);
1086 updateSingleIndex(indexId
, file
, null, fileContent
);
1088 catch (StorageException e
) {
1090 requestRebuild(indexId
);
1095 IndexingStamp
.flushCache();
1097 if (!affectedIndices
.isEmpty()) {
1099 myFileContentAttic
.offer(file
);
1100 iterateIndexableFiles(file
, new Processor
<VirtualFile
>() {
1101 public boolean process(final VirtualFile file
) {
1102 myFilesToUpdate
.add(file
);
1108 // first check if there is an unprocessed content from previous events
1109 byte[] content
= myFileContentAttic
.remove(file
);
1111 if (content
== null) {
1112 content
= file
.contentsToByteArray();
1115 catch (IOException e
) {
1116 content
= ArrayUtil
.EMPTY_BYTE_ARRAY
;
1118 final FileContent fc
= new FileContent(file
, content
);
1119 synchronized (myFutureInvalidations
) {
1120 final InvalidationTask invalidator
= new InvalidationTask(file
) {
1122 Throwable unexpectedError
= null;
1123 for (ID
<?
, ?
> indexId
: affectedIndices
) {
1125 updateSingleIndex(indexId
, file
, null, fc
);
1127 catch (StorageException e
) {
1129 requestRebuild(indexId
);
1131 catch (ProcessCanceledException ignored
) {
1133 catch (Throwable e
) {
1135 if (unexpectedError
== null) {
1136 unexpectedError
= e
;
1141 IndexingStamp
.flushCache();
1142 if (unexpectedError
!= null) {
1143 LOG
.error(unexpectedError
);
1148 myFutureInvalidations
.offer(invalidator
);
1155 public void ensureAllInvalidateTasksCompleted() {
1156 final boolean doProgressThing
;
1159 synchronized (myFutureInvalidations
) {
1160 size
= myFutureInvalidations
.size();
1161 if (size
== 0) return;
1163 doProgressThing
= size
> 1 && ApplicationManager
.getApplication().isDispatchThread();
1166 final Task
.Modal task
= new Task
.Modal(null, "Invalidating Index Entries", false) {
1167 public void run(@NotNull final ProgressIndicator indicator
) {
1168 indicator
.setText("");
1172 synchronized (myFutureInvalidations
) {
1173 r
= myFutureInvalidations
.poll();
1176 if (r
== null) return;
1177 indicator
.setFraction(((double)count
++)/size
);
1178 indicator
.setText2(r
.getSubj().getPresentableUrl());
1184 if (doProgressThing
) {
1188 ProgressIndicator indicator
= ProgressManager
.getInstance().getProgressIndicator();
1189 task
.run(indicator
!= null ? indicator
: new EmptyProgressIndicator());
1193 private void iterateIndexableFiles(final VirtualFile file
, final Processor
<VirtualFile
> processor
) {
1194 if (file
.isDirectory()) {
1195 final ContentIterator iterator
= new ContentIterator() {
1196 public boolean processFile(final VirtualFile fileOrDir
) {
1197 if (!fileOrDir
.isDirectory()) {
1198 processor
.process(fileOrDir
);
1204 for (IndexableFileSet set
: myIndexableSets
) {
1205 set
.iterateIndexableFilesIn(file
, iterator
);
1209 for (IndexableFileSet set
: myIndexableSets
) {
1210 if (set
.isInSet(file
)) {
1211 processor
.process(file
);
1218 public VirtualFile
[] queryNeededFiles() {
1219 synchronized (myFilesToUpdate
) {
1220 return myFilesToUpdate
.toArray(new VirtualFile
[myFilesToUpdate
.size()]);
1224 public void processFile(final com
.intellij
.ide
.startup
.FileContent fileContent
) {
1225 ensureAllInvalidateTasksCompleted();
1226 processFileImpl(fileContent
);
1229 private final Semaphore myForceUpdateSemaphore
= new Semaphore();
1231 public void forceUpdate() {
1232 ensureAllInvalidateTasksCompleted();
1234 final VirtualFile
[] files
= queryNeededFiles();
1235 if (files
.length
> 0) {
1236 myForceUpdateSemaphore
.down();
1238 for (VirtualFile file
: files
) {
1239 processFileImpl(new com
.intellij
.ide
.startup
.FileContent(file
));
1243 myForceUpdateSemaphore
.up();
1244 myForceUpdateSemaphore
.waitFor(); // possibly wait until another thread completes indexing
1249 public void updatingDone() {
1252 public void canceled() {
1255 private void processFileImpl(final com
.intellij
.ide
.startup
.FileContent fileContent
) {
1256 final VirtualFile file
= fileContent
.getVirtualFile();
1257 final boolean reallyRemoved
= myFilesToUpdate
.remove(file
);
1258 if (reallyRemoved
&& file
.isValid()) {
1259 indexFileContent(fileContent
);
1264 private class UnindexedFilesFinder
implements CollectingContentIterator
{
1265 private final List
<VirtualFile
> myFiles
= new ArrayList
<VirtualFile
>();
1266 private final Collection
<ID
<?
, ?
>> myIndexIds
;
1267 private final Collection
<ID
<?
, ?
>> mySkipContentLoading
;
1268 private final ProgressIndicator myProgressIndicator
;
1270 private UnindexedFilesFinder(final Collection
<ID
<?
, ?
>> indexIds
) {
1271 myIndexIds
= new ArrayList
<ID
<?
, ?
>>();
1272 mySkipContentLoading
= new ArrayList
<ID
<?
, ?
>>();
1273 for (ID
<?
, ?
> indexId
: indexIds
) {
1274 if (myNeedContentLoading
.contains(indexId
)) {
1275 myIndexIds
.add(indexId
);
1278 mySkipContentLoading
.add(indexId
);
1281 myProgressIndicator
= ProgressManager
.getInstance().getProgressIndicator();
1284 public List
<VirtualFile
> getFiles() {
1288 public boolean processFile(final VirtualFile file
) {
1289 if (!file
.isDirectory()) {
1290 if (file
instanceof NewVirtualFile
&& ((NewVirtualFile
)file
).getFlag(ALREADY_PROCESSED
)) {
1294 if (file
instanceof VirtualFileWithId
) {
1295 boolean oldStuff
= true;
1296 if (!SingleRootFileViewProvider
.isTooLarge(file
)) {
1297 for (ID
<?
, ?
> indexId
: myIndexIds
) {
1299 if (myFileContentAttic
.containsContent(file
) ?
getInputFilter(indexId
).acceptInput(file
) : shouldIndexFile(file
, indexId
)) {
1305 catch (RuntimeException e
) {
1306 final Throwable cause
= e
.getCause();
1307 if (cause
instanceof IOException
|| cause
instanceof StorageException
) {
1309 requestRebuild(indexId
);
1317 FileContent fileContent
= null;
1318 for (ID
<?
, ?
> indexId
: mySkipContentLoading
) {
1319 if (shouldIndexFile(file
, indexId
)) {
1322 if (fileContent
== null) {
1323 fileContent
= new FileContent(file
);
1325 updateSingleIndex(indexId
, file
, fileContent
, null);
1327 catch (StorageException e
) {
1329 requestRebuild(indexId
);
1333 IndexingStamp
.flushCache();
1335 if (oldStuff
&& file
instanceof NewVirtualFile
) {
1336 ((NewVirtualFile
)file
).setFlag(ALREADY_PROCESSED
, true);
1341 if (myProgressIndicator
!= null) {
1342 myProgressIndicator
.setText("Scanning files to index");
1343 myProgressIndicator
.setText2(file
.getPresentableUrl());
1350 private boolean shouldUpdateIndex(final VirtualFile file
, final ID
<?
, ?
> indexId
) {
1351 return getInputFilter(indexId
).acceptInput(file
) &&
1352 (isMock(file
) || IndexingStamp
.isFileIndexed(file
, indexId
, IndexInfrastructure
.getIndexCreationStamp(indexId
)));
1355 private boolean shouldIndexFile(final VirtualFile file
, final ID
<?
, ?
> indexId
) {
1356 return getInputFilter(indexId
).acceptInput(file
) &&
1357 (isMock(file
) || !IndexingStamp
.isFileIndexed(file
, indexId
, IndexInfrastructure
.getIndexCreationStamp(indexId
)));
1360 private static boolean isMock(final VirtualFile file
) {
1361 return !(file
instanceof NewVirtualFile
);
1364 public CollectingContentIterator
createContentIterator() {
1365 return new UnindexedFilesFinder(myIndices
.keySet());
1368 public void registerIndexableSet(IndexableFileSet set
) {
1369 myIndexableSets
.add(set
);
1372 public void removeIndexableSet(IndexableFileSet set
) {
1373 myChangedFilesUpdater
.forceUpdate();
1374 myIndexableSets
.remove(set
);
1378 private static PsiFile
findLatestKnownPsiForUncomittedDocument(Document doc
) {
1379 PsiFile target
= null;
1380 long modStamp
= -1L;
1382 for (Project project
: ProjectManager
.getInstance().getOpenProjects()) {
1383 final PsiDocumentManager pdm
= PsiDocumentManager
.getInstance(project
);
1384 final PsiFile file
= pdm
.getCachedPsiFile(doc
);
1385 if (file
!= null && file
.getModificationStamp() > modStamp
) {
1386 final ProjectFileIndex index
= ProjectRootManager
.getInstance(project
).getFileIndex();
1387 final VirtualFile vFile
= file
.getVirtualFile();
1388 if (vFile
!= null && (index
.isInContent(vFile
) || index
.isInLibrarySource(vFile
))) {
1390 modStamp
= file
.getModificationStamp();
1393 else if (file
!= null && file
.getModificationStamp() == modStamp
) {
1394 if (target
instanceof PsiPlainTextFile
&& !(file
instanceof PsiPlainTextFile
)) {
1403 private static class IndexableFilesFilter
implements InputFilter
{
1404 private final InputFilter myDelegate
;
1406 private IndexableFilesFilter(InputFilter delegate
) {
1407 myDelegate
= delegate
;
1410 public boolean acceptInput(final VirtualFile file
) {
1411 return file
instanceof VirtualFileWithId
&& myDelegate
.acceptInput(file
);
1415 private static void cleanupProcessedFlag() {
1416 final VirtualFile
[] roots
= ManagingFS
.getInstance().getRoots();
1417 for (VirtualFile root
: roots
) {
1418 cleanProcessedFlag(root
);
1422 private static void cleanProcessedFlag(final VirtualFile file
) {
1423 if (!(file
instanceof NewVirtualFile
)) return;
1425 final NewVirtualFile nvf
= (NewVirtualFile
)file
;
1426 if (file
.isDirectory()) {
1427 for (VirtualFile child
: nvf
.getCachedChildren()) {
1428 cleanProcessedFlag(child
);
1432 /* nvf.clearCachedFileType(); */
1433 nvf
.setFlag(ALREADY_PROCESSED
, false);
1437 private static class StorageGuard
{
1438 private int myHolds
= 0;
1440 public interface Holder
{
1444 private final Holder myTrueHolder
= new Holder() {
1445 public void leave() {
1446 StorageGuard
.this.leave(true);
1449 private final Holder myFalseHolder
= new Holder() {
1450 public void leave() {
1451 StorageGuard
.this.leave(false);
1455 public synchronized Holder
enter(boolean mode
) {
1457 while (myHolds
< 0) {
1461 catch (InterruptedException ignored
) {
1465 return myTrueHolder
;
1468 while (myHolds
> 0) {
1472 catch (InterruptedException ignored
) {
1476 return myFalseHolder
;
1480 private synchronized void leave(boolean mode
) {
1481 myHolds
+= (mode?
-1 : 1);