4 package com
.intellij
.psi
.stubs
;
6 import com
.intellij
.lang
.ASTNode
;
7 import com
.intellij
.openapi
.application
.ApplicationManager
;
8 import com
.intellij
.openapi
.application
.ModalityState
;
9 import com
.intellij
.openapi
.components
.*;
10 import com
.intellij
.openapi
.diagnostic
.Logger
;
11 import com
.intellij
.openapi
.extensions
.Extensions
;
12 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
13 import com
.intellij
.openapi
.project
.Project
;
14 import com
.intellij
.openapi
.util
.io
.FileUtil
;
15 import com
.intellij
.openapi
.vfs
.VirtualFile
;
16 import com
.intellij
.openapi
.vfs
.newvfs
.ManagingFS
;
17 import com
.intellij
.openapi
.vfs
.newvfs
.persistent
.PersistentFS
;
18 import com
.intellij
.psi
.PsiElement
;
19 import com
.intellij
.psi
.PsiFile
;
20 import com
.intellij
.psi
.PsiManager
;
21 import com
.intellij
.psi
.PsiPlainTextFile
;
22 import com
.intellij
.psi
.impl
.source
.PsiFileImpl
;
23 import com
.intellij
.psi
.impl
.source
.PsiFileWithStubSupport
;
24 import com
.intellij
.psi
.search
.GlobalSearchScope
;
25 import com
.intellij
.psi
.tree
.IElementType
;
26 import com
.intellij
.psi
.tree
.IStubFileElementType
;
27 import com
.intellij
.util
.indexing
.*;
28 import com
.intellij
.util
.io
.DataExternalizer
;
29 import com
.intellij
.util
.io
.DataInputOutputUtil
;
30 import gnu
.trove
.TIntArrayList
;
31 import gnu
.trove
.TObjectIntHashMap
;
32 import org
.jetbrains
.annotations
.NotNull
;
34 import java
.io
.DataInput
;
35 import java
.io
.DataOutput
;
37 import java
.io
.IOException
;
39 import java
.util
.concurrent
.atomic
.AtomicInteger
;
40 import java
.util
.concurrent
.locks
.Lock
;
43 name
= "FileBasedIndex",
44 roamingType
= RoamingType
.DISABLED
,
48 file
= "$APP_CONFIG$/stubIndex.xml")
51 public class StubIndexImpl
extends StubIndex
implements ApplicationComponent
, PersistentStateComponent
<StubIndexState
> {
52 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.stubs.StubIndexImpl");
53 private final Map
<StubIndexKey
<?
,?
>, MyIndex
<?
>> myIndices
= new HashMap
<StubIndexKey
<?
,?
>, MyIndex
<?
>>();
54 private final TObjectIntHashMap
<ID
<?
, ?
>> myIndexIdToVersionMap
= new TObjectIntHashMap
<ID
<?
, ?
>>();
56 public static final int OK
= 1;
57 public static final int NEED_REBUILD
= 2;
58 public static final int REBUILD_IN_PROGRESS
= 3;
59 private final AtomicInteger myRebuildStatus
= new AtomicInteger(OK
);
61 private StubIndexState myPreviouslyRegistered
;
63 public StubIndexImpl() throws IOException
{
64 final StubIndexExtension
<?
, ?
>[] extensions
= Extensions
.getExtensions(StubIndexExtension
.EP_NAME
);
65 boolean needRebuild
= false;
66 for (StubIndexExtension extension
: extensions
) {
67 //noinspection unchecked
68 needRebuild
|= registerIndexer(extension
);
71 myRebuildStatus
.set(NEED_REBUILD
);
73 dropUnregisteredIndices();
76 private <K
> boolean registerIndexer(final StubIndexExtension
<K
, ?
> extension
) throws IOException
{
77 final StubIndexKey
<K
, ?
> indexKey
= extension
.getKey();
78 final int version
= extension
.getVersion();
79 myIndexIdToVersionMap
.put(indexKey
, version
);
80 final File versionFile
= IndexInfrastructure
.getVersionFile(indexKey
);
81 final boolean versionFileExisted
= versionFile
.exists();
82 final File indexRootDir
= IndexInfrastructure
.getIndexRootDir(indexKey
);
83 boolean needRebuild
= false;
84 if (IndexInfrastructure
.versionDiffers(versionFile
, version
)) {
85 final String
[] children
= indexRootDir
.list();
86 // rebuild only if there exists what to rebuild
87 needRebuild
= versionFileExisted
|| children
!= null && children
.length
> 0;
89 LOG
.info("Version has changed for stub index " + extension
.getKey() + ". The index will be rebuilt.");
91 FileUtil
.delete(indexRootDir
);
92 IndexInfrastructure
.rewriteVersion(versionFile
, version
);
95 for (int attempt
= 0; attempt
< 2; attempt
++) {
97 final MapIndexStorage
<K
, TIntArrayList
> storage
= new MapIndexStorage
<K
, TIntArrayList
>(IndexInfrastructure
.getStorageFile(indexKey
), extension
.getKeyDescriptor(), new StubIdExternalizer(), 2 * 1024);
98 final MemoryIndexStorage
<K
, TIntArrayList
> memStorage
= new MemoryIndexStorage
<K
, TIntArrayList
>(storage
);
99 myIndices
.put(indexKey
, new MyIndex
<K
>(memStorage
));
102 catch (IOException e
) {
105 FileUtil
.delete(indexRootDir
);
106 IndexInfrastructure
.rewriteVersion(versionFile
, version
);
112 private static class StubIdExternalizer
implements DataExternalizer
<TIntArrayList
> {
113 public void save(final DataOutput out
, final TIntArrayList value
) throws IOException
{
114 int size
= value
.size();
116 DataInputOutputUtil
.writeSINT(out
, Integer
.MAX_VALUE
);
118 else if (size
== 1) {
119 DataInputOutputUtil
.writeSINT(out
, -value
.get(0));
122 DataInputOutputUtil
.writeSINT(out
, size
);
123 for (int i
= 0; i
< size
; i
++) {
124 DataInputOutputUtil
.writeINT(out
, value
.get(i
));
129 public TIntArrayList
read(final DataInput in
) throws IOException
{
130 int size
= DataInputOutputUtil
.readSINT(in
);
131 if (size
== Integer
.MAX_VALUE
) {
132 return new TIntArrayList();
134 else if (size
<= 0) {
135 TIntArrayList result
= new TIntArrayList(1);
140 TIntArrayList result
= new TIntArrayList(size
);
141 for (int i
= 0; i
< size
; i
++) {
142 result
.add(DataInputOutputUtil
.readINT(in
));
149 public <Key
, Psi
extends PsiElement
> Collection
<Psi
> get(@NotNull final StubIndexKey
<Key
, Psi
> indexKey
, @NotNull final Key key
, final Project project
,
150 final GlobalSearchScope scope
) {
151 checkRebuild(project
);
153 FileBasedIndex
.getInstance().ensureUpToDate(StubUpdatingIndex
.INDEX_ID
, project
);
155 final PersistentFS fs
= (PersistentFS
)ManagingFS
.getInstance();
156 final PsiManager psiManager
= PsiManager
.getInstance(project
);
158 final List
<Psi
> result
= new ArrayList
<Psi
>();
159 final MyIndex
<Key
> index
= (MyIndex
<Key
>)myIndices
.get(indexKey
);
163 // disable up-to-date check to avoid locks on attempt to acquire index write lock while holding at the same time the readLock for this index
164 FileBasedIndex
.getInstance().disableUpToDateCheckForCurrentThread();
165 index
.getReadLock().lock();
166 final ValueContainer
<TIntArrayList
> container
= index
.getData(key
);
168 container
.forEach(new ValueContainer
.ContainerAction
<TIntArrayList
>() {
169 public void perform(final int id
, final TIntArrayList value
) {
170 final VirtualFile file
= IndexInfrastructure
.findFileById(fs
, id
);
171 if (file
!= null && (scope
== null || scope
.contains(file
))) {
172 StubTree stubTree
= null;
174 final PsiFile _psifile
= psiManager
.findFile(file
);
175 PsiFileWithStubSupport psiFile
= null;
177 if (_psifile
!= null && !(_psifile
instanceof PsiPlainTextFile
)) {
178 if (_psifile
instanceof PsiFileWithStubSupport
) {
179 psiFile
= (PsiFileWithStubSupport
)_psifile
;
180 stubTree
= psiFile
.getStubTree();
181 if (stubTree
== null && psiFile
instanceof PsiFileImpl
) {
182 stubTree
= ((PsiFileImpl
)psiFile
).calcStubTree();
187 if (stubTree
!= null || psiFile
!= null) {
188 if (stubTree
== null) {
189 stubTree
= StubTree
.readFromVFile(project
, file
);
190 if (stubTree
!= null) {
191 final List
<StubElement
<?
>> plained
= stubTree
.getPlainList();
192 for (int i
= 0; i
< value
.size(); i
++) {
193 final StubElement
<?
> stub
= plained
.get(value
.get(i
));
194 final ASTNode tree
= psiFile
.findTreeForStub(stubTree
, stub
);
197 if (tree
.getElementType() == stubType(stub
)) {
198 result
.add((Psi
)tree
.getPsi());
201 String persistedStubTree
= ((PsiFileStubImpl
)stubTree
.getRoot()).printTree();
203 String stubTreeJustBuilt
=
204 ((PsiFileStubImpl
)((IStubFileElementType
)((PsiFileImpl
)psiFile
).getContentElementType()).getBuilder()
205 .buildStubTree(psiFile
)).printTree();
207 StringBuilder builder
= new StringBuilder();
208 builder
.append("Oops\n");
211 builder
.append("Recorded stub:-----------------------------------\n");
212 builder
.append(persistedStubTree
);
213 builder
.append("\nAST built stub: ------------------------------------\n");
214 builder
.append(stubTreeJustBuilt
);
215 builder
.append("\n");
216 LOG
.info(builder
.toString());
218 // requestReindex() may want to acquire write lock (for indices not requiring content loading)
219 // thus, because here we are under read lock, need to use invoke later
220 ApplicationManager
.getApplication().invokeLater(new Runnable() {
222 FileBasedIndex
.getInstance().requestReindex(file
);
224 }, ModalityState
.NON_MODAL
);
231 final List
<StubElement
<?
>> plained
= stubTree
.getPlainList();
232 for (int i
= 0; i
< value
.size(); i
++) {
233 result
.add((Psi
)plained
.get(value
.get(i
)).getPsi());
242 index
.getReadLock().unlock();
243 FileBasedIndex
.getInstance().enableUpToDateCheckForCurrentThread();
246 catch (StorageException e
) {
247 forceRebuild(e
, project
);
249 catch (RuntimeException e
) {
250 final Throwable cause
= e
.getCause();
251 if (cause
instanceof IOException
|| cause
instanceof StorageException
) {
252 forceRebuild(e
, project
);
262 private static IElementType
stubType(final StubElement
<?
> stub
) {
263 if (stub
instanceof PsiFileStub
) {
264 return ((PsiFileStub
)stub
).getType();
267 return stub
.getStubType();
270 private void forceRebuild(Throwable e
, Project project
) {
272 myRebuildStatus
.set(NEED_REBUILD
);
273 checkRebuild(project
);
276 private void checkRebuild(@NotNull Project project
) {
277 if (myRebuildStatus
.compareAndSet(NEED_REBUILD
, REBUILD_IN_PROGRESS
)) {
278 StubUpdatingIndex
.scheduleStubIndicesRebuild(new Runnable() {
280 myRebuildStatus
.compareAndSet(REBUILD_IN_PROGRESS
, OK
);
284 if (myRebuildStatus
.get() == REBUILD_IN_PROGRESS
) {
285 throw new ProcessCanceledException();
289 public <K
> Collection
<K
> getAllKeys(final StubIndexKey
<K
, ?
> indexKey
, @NotNull Project project
) {
290 checkRebuild(project
);
291 FileBasedIndex
.getInstance().ensureUpToDate(StubUpdatingIndex
.INDEX_ID
, project
);
293 final MyIndex
<K
> index
= (MyIndex
<K
>)myIndices
.get(indexKey
);
295 return index
.getAllKeys();
297 catch (StorageException e
) {
298 forceRebuild(e
, project
);
300 catch (RuntimeException e
) {
301 final Throwable cause
= e
.getCause();
302 if (cause
instanceof IOException
|| cause
instanceof StorageException
) {
303 forceRebuild(e
, project
);
307 return Collections
.emptyList();
311 public String
getComponentName() {
312 return "Stub.IndexManager";
315 public void initComponent() {
318 public void disposeComponent() {
319 // This index must be disposed only after StubUpdatingIndex is disposed
320 // To ensure this, disposing is done explicitly from StubUpdatingIndex by calling dispose() method
321 // do not call this method here to avoid double-disposal
324 public void dispose() {
325 for (UpdatableIndex index
: myIndices
.values()) {
330 public void setDataBufferingEnabled(final boolean enabled
) {
331 for (UpdatableIndex index
: myIndices
.values()) {
332 final IndexStorage indexStorage
= ((MapReduceIndex
)index
).getStorage();
333 ((MemoryIndexStorage
)indexStorage
).setBufferingEnabled(enabled
);
338 public void clearAllIndices() {
339 for (UpdatableIndex index
: myIndices
.values()) {
343 catch (StorageException e
) {
345 throw new RuntimeException(e
);
350 private void dropUnregisteredIndices() {
351 final Set
<String
> indicesToDrop
= new HashSet
<String
>(myPreviouslyRegistered
!= null? myPreviouslyRegistered
.registeredIndices
: Collections
.<String
>emptyList());
352 for (ID
<?
, ?
> key
: myIndices
.keySet()) {
353 indicesToDrop
.remove(key
.toString());
356 for (String s
: indicesToDrop
) {
357 FileUtil
.delete(IndexInfrastructure
.getIndexRootDir(ID
.create(s
)));
361 public StubIndexState
getState() {
362 return new StubIndexState(myIndices
.keySet());
365 public void loadState(final StubIndexState state
) {
366 myPreviouslyRegistered
= state
;
369 public Lock
getWriteLock(StubIndexKey indexKey
) {
370 return myIndices
.get(indexKey
).getWriteLock();
373 public Collection
<StubIndexKey
> getAllStubIndexKeys() {
374 return Collections
.<StubIndexKey
>unmodifiableCollection(myIndices
.keySet());
377 public <K
> void updateIndex(StubIndexKey key
, int fileId
, Map
<K
, TIntArrayList
> oldValues
, Map
<K
, TIntArrayList
> newValues
) {
379 MyIndex
<K
> index
= (MyIndex
<K
>)myIndices
.get(key
);
380 index
.updateWithMap(fileId
, newValues
, oldValues
.keySet());
382 catch (StorageException e
) {
384 myRebuildStatus
.set(NEED_REBUILD
);
388 private static class MyIndex
<K
> extends MapReduceIndex
<K
, TIntArrayList
, Void
> {
389 public MyIndex(final IndexStorage
<K
, TIntArrayList
> storage
) {
390 super(null, null, storage
);
393 public void updateWithMap(final int inputId
, final Map
<K
, TIntArrayList
> newData
, Collection
<K
> oldKeys
) throws StorageException
{
394 getWriteLock().lock();
396 super.updateWithMap(inputId
, newData
, oldKeys
);
399 getWriteLock().unlock();