1 package com
.intellij
.psi
.impl
.source
;
3 import com
.intellij
.extapi
.psi
.StubBasedPsiElementBase
;
4 import com
.intellij
.ide
.startup
.FileContent
;
5 import com
.intellij
.ide
.util
.EditSourceUtil
;
6 import com
.intellij
.lang
.ASTFactory
;
7 import com
.intellij
.lang
.ASTNode
;
8 import com
.intellij
.lang
.Language
;
9 import com
.intellij
.navigation
.ItemPresentation
;
10 import com
.intellij
.openapi
.application
.ApplicationManager
;
11 import com
.intellij
.openapi
.application
.ModalityState
;
12 import com
.intellij
.openapi
.diagnostic
.Logger
;
13 import com
.intellij
.openapi
.editor
.Document
;
14 import com
.intellij
.openapi
.editor
.colors
.TextAttributesKey
;
15 import com
.intellij
.openapi
.fileEditor
.FileDocumentManager
;
16 import com
.intellij
.openapi
.fileTypes
.FileType
;
17 import com
.intellij
.openapi
.project
.Project
;
18 import com
.intellij
.openapi
.util
.Key
;
19 import com
.intellij
.openapi
.util
.TextRange
;
20 import com
.intellij
.openapi
.vcs
.FileStatus
;
21 import com
.intellij
.openapi
.vcs
.FileStatusManager
;
22 import com
.intellij
.openapi
.vfs
.VirtualFile
;
23 import com
.intellij
.openapi
.vfs
.VirtualFileWithId
;
24 import com
.intellij
.psi
.*;
25 import com
.intellij
.psi
.impl
.*;
26 import com
.intellij
.psi
.impl
.cache
.impl
.CacheUtil
;
27 import com
.intellij
.psi
.impl
.file
.PsiFileImplUtil
;
28 import com
.intellij
.psi
.impl
.source
.codeStyle
.CodeEditUtil
;
29 import com
.intellij
.psi
.impl
.source
.parsing
.ChameleonTransforming
;
30 import com
.intellij
.psi
.impl
.source
.resolve
.FileContextUtil
;
31 import com
.intellij
.psi
.impl
.source
.tree
.*;
32 import com
.intellij
.psi
.scope
.PsiScopeProcessor
;
33 import com
.intellij
.psi
.search
.GlobalSearchScope
;
34 import com
.intellij
.psi
.search
.PsiElementProcessor
;
35 import com
.intellij
.psi
.search
.SearchScope
;
36 import com
.intellij
.psi
.stubs
.*;
37 import com
.intellij
.psi
.tree
.IElementType
;
38 import com
.intellij
.psi
.tree
.IStubFileElementType
;
39 import com
.intellij
.reference
.SoftReference
;
40 import com
.intellij
.util
.IncorrectOperationException
;
41 import com
.intellij
.util
.PatchedSoftReference
;
42 import com
.intellij
.util
.PatchedWeakReference
;
43 import com
.intellij
.util
.indexing
.FileBasedIndex
;
44 import com
.intellij
.util
.text
.CharArrayUtil
;
45 import org
.jetbrains
.annotations
.NotNull
;
46 import org
.jetbrains
.annotations
.Nullable
;
49 import java
.lang
.ref
.Reference
;
50 import java
.lang
.reflect
.Array
;
51 import java
.util
.ArrayList
;
52 import java
.util
.Iterator
;
53 import java
.util
.List
;
56 public abstract class PsiFileImpl
extends ElementBase
implements PsiFileEx
, PsiFileWithStubSupport
{
57 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.impl.source.PsiFileImpl");
59 private IElementType myElementType
;
60 protected IElementType myContentElementType
;
62 protected PsiFile myOriginalFile
= null;
63 private final FileViewProvider myViewProvider
;
64 private static final Key
<Document
> HARD_REFERENCE_TO_DOCUMENT
= new Key
<Document
>("HARD_REFERENCE_TO_DOCUMENT");
65 private final Object myStubLock
= PsiLock
.LOCK
;
66 private SoftReference
<StubTree
> myStub
;
67 protected final PsiManagerEx myManager
;
68 private volatile Object myTreeElementPointer
; // SoftReference/WeakReference to RepositoryTreeElement when has repository id, RepositoryTreeElement otherwise
69 public static final Key
<Boolean
> BUILDING_STUB
= new Key
<Boolean
>("Don't use stubs mark!");
71 protected PsiFileImpl(@NotNull IElementType elementType
, IElementType contentElementType
, @NotNull FileViewProvider provider
) {
73 init(elementType
, contentElementType
);
76 protected PsiFileImpl(@NotNull FileViewProvider provider
) {
77 myManager
= (PsiManagerEx
)provider
.getManager();
78 myViewProvider
= provider
;
81 public void setContentElementType(final IElementType contentElementType
) {
82 myContentElementType
= contentElementType
;
85 public IElementType
getContentElementType() {
86 return myContentElementType
;
89 protected void init(@NotNull final IElementType elementType
, final IElementType contentElementType
) {
90 myElementType
= elementType
;
91 myContentElementType
= contentElementType
;
94 public TreeElement
createContentLeafElement(CharSequence leafText
) {
95 return ASTFactory
.leaf(myContentElementType
, leafText
);
98 public boolean isDirectory() {
102 public FileElement
getTreeElement() {
103 final FileElement noLockAttempt
= (FileElement
)_getTreeElement();
104 if (noLockAttempt
!= null) return noLockAttempt
;
106 synchronized (myStubLock
) {
107 return getTreeElementNoLock();
111 public FileElement
getTreeElementNoLock() {
112 if (!getViewProvider().isPhysical() && _getTreeElement() == null) {
113 setTreeElement(loadTreeElement());
115 return (FileElement
)_getTreeElement();
118 protected boolean isKeepTreeElementByHardReference() {
119 return !getViewProvider().isEventSystemEnabled();
122 private ASTNode
_getTreeElement() {
123 final Object pointer
= myTreeElementPointer
;
124 if (pointer
instanceof FileElement
) {
125 return (FileElement
)pointer
;
127 else if (pointer
instanceof Reference
) {
128 FileElement treeElement
= (FileElement
)((Reference
)pointer
).get();
129 if (treeElement
!= null) return treeElement
;
131 synchronized (myStubLock
) {
132 if (myTreeElementPointer
== pointer
) {
133 myTreeElementPointer
= null;
142 public VirtualFile
getVirtualFile() {
143 return getViewProvider().isEventSystemEnabled() ?
getViewProvider().getVirtualFile() : null;
146 public boolean processChildren(final PsiElementProcessor
<PsiFileSystemItem
> processor
) {
150 public boolean isValid() {
151 if (!getViewProvider().isPhysical()) return true; // "dummy" file
152 final VirtualFile vFile
= getViewProvider().getVirtualFile();
153 return vFile
.isValid() && isPsiUpToDate(vFile
);
155 //FileViewProvider viewProvider = getViewProvider();
156 //if (!viewProvider.isPhysical()) return true; // "dummy" file
157 //final VirtualFile vFile = viewProvider.getVirtualFile();
158 //if (!vFile.isValid() || !isPsiUpToDate(vFile)) return false;
159 //PsiManager manager = getManager();
160 //boolean valid = manager != null && !manager.getProject().isDisposed();
164 protected boolean isPsiUpToDate(VirtualFile vFile
) {
165 final FileViewProvider provider
= myManager
.findViewProvider(vFile
);
166 return provider
.getPsi(getLanguage()) == this || provider
.getPsi(provider
.getBaseLanguage()) == this;
169 public boolean isContentsLoaded() {
170 return _getTreeElement() != null;
173 public FileElement
loadTreeElement() {
174 synchronized (myStubLock
) {
175 FileElement treeElement
= (FileElement
)_getTreeElement();
176 if (treeElement
!= null) return treeElement
;
178 final FileViewProvider viewProvider
= getViewProvider();
179 if (viewProvider
.isPhysical() && myManager
.isAssertOnFileLoading(viewProvider
.getVirtualFile())) {
180 LOG
.error("Access to tree elements not allowed in tests." + viewProvider
.getVirtualFile().getPresentableUrl());
183 // load document outside lock for better performance
184 final Document document
= viewProvider
.isEventSystemEnabled() ? viewProvider
.getDocument() : null;
185 //synchronized (PsiLock.LOCK) {
186 treeElement
= createFileElement(viewProvider
.getContents());
187 if (document
!= null) {
188 treeElement
.putUserData(HARD_REFERENCE_TO_DOCUMENT
, document
);
190 setTreeElement(treeElement
);
191 treeElement
.setPsi(this);
194 StubTree stub
= derefStub();
196 final Iterator
<StubElement
<?
>> stubs
= stub
.getPlainList().iterator();
197 stubs
.next(); // Skip file stub;
198 switchFromStubToAST(treeElement
, stubs
);
203 if (getViewProvider().isEventSystemEnabled()) {
204 ((PsiDocumentManagerImpl
)PsiDocumentManager
.getInstance(myManager
.getProject())).contentsLoaded(this);
206 if (LOG
.isDebugEnabled() && getViewProvider().isPhysical()) {
207 LOG
.debug("Loaded text for file " + getViewProvider().getVirtualFile().getPresentableUrl());
214 public ASTNode
findTreeForStub(StubTree tree
, StubElement
<?
> stub
) {
215 final Iterator
<StubElement
<?
>> stubs
= tree
.getPlainList().iterator();
216 final StubElement
<?
> root
= stubs
.next();
217 final CompositeElement ast
= calcTreeElement();
218 if (root
== stub
) return ast
;
220 return findTreeForStub(ast
, stubs
, stub
);
223 private static ASTNode
findTreeForStub(ASTNode tree
, final Iterator
<StubElement
<?
>> stubs
, final StubElement stub
) {
224 if (tree
instanceof ChameleonElement
) {
225 tree
= ChameleonTransforming
.transform((ChameleonElement
)tree
);
228 final IElementType type
= tree
.getElementType();
230 if (type
instanceof IStubElementType
&& ((IStubElementType
) type
).shouldCreateStub(tree
)) {
231 final StubElement curStub
= stubs
.next();
232 if (curStub
== stub
) return tree
;
235 for (ASTNode node
: tree
.getChildren(null)) {
236 final ASTNode treeForStub
= findTreeForStub(node
, stubs
, stub
);
237 if (treeForStub
!= null) return treeForStub
;
244 private void switchFromStubToAST(ASTNode tree
, Iterator
<StubElement
<?
>> stubs
) {
245 if (tree
instanceof ChameleonElement
) {
246 tree
= ChameleonTransforming
.transform((ChameleonElement
)tree
);
249 final IElementType type
= tree
.getElementType();
251 if (type
instanceof IStubElementType
&& ((IStubElementType
) type
).shouldCreateStub(tree
)) {
252 final StubElement stub
= stubs
.next();
253 if (stub
.getStubType() != tree
.getElementType()) {
255 assert false: "Stub and PSI element type mismatch in " + getName() + ": stub " + stub
+ ", AST " + tree
.getElementType();
257 final PsiElement psi
= stub
.getPsi();
258 ((CompositeElement
)tree
).setPsi(psi
);
259 final StubBasedPsiElementBase
<?
> base
= (StubBasedPsiElementBase
)psi
;
262 if (LOG
.isDebugEnabled()) {
263 LOG
.debug("Bound " + base
+ " to " + stub
);
267 for (ASTNode node
: tree
.getChildren(null)) {
268 switchFromStubToAST(node
, stubs
);
272 protected FileElement
createFileElement(final CharSequence docText
) {
274 final CompositeElement xxx
= ASTFactory
.composite(myElementType
);
275 if (!(xxx
instanceof FileElement
)) {
278 final FileElement treeElement
= (FileElement
)xxx
;
279 if (CacheUtil
.isCopy(this)) {
280 treeElement
.setCharTable(new IdentityCharTable());
283 TreeElement contentElement
= createContentLeafElement(treeElement
.getCharTable().intern(docText
, 0, docText
.length()));
284 treeElement
.rawAddChildren(contentElement
);
288 public void unloadContent() {
289 LOG
.assertTrue(getTreeElement() != null);
291 myViewProvider
.beforeContentsSynchronized();
292 setTreeElement(null);
293 synchronized (myStubLock
) {
298 public void clearCaches() {}
300 public String
getText() {
301 return getViewProvider().getContents().toString();
304 public int getTextLength() {
305 final ASTNode tree
= _getTreeElement();
306 if (tree
!= null) return tree
.getTextLength();
308 return getViewProvider().getContents().length();
311 public TextRange
getTextRange() {
312 return new TextRange(0, getTextLength());
315 public PsiElement
getNextSibling() {
316 return SharedPsiElementImplUtil
.getNextSibling(this);
319 public PsiElement
getPrevSibling() {
320 return SharedPsiElementImplUtil
.getPrevSibling(this);
323 public long getModificationStamp() {
324 return getViewProvider().getModificationStamp();
327 public void subtreeChanged() {
329 getViewProvider().rootChanged(this);
332 private void doClearCaches() {
333 final FileElement tree
= getTreeElement();
335 myTreeElementPointer
= tree
;
337 tree
.putUserData(STUB_TREE_IN_PARSED_TREE
, null);
340 synchronized (myStubLock
) {
347 @SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException", "CloneDoesntCallSuperClone"})
348 protected PsiFileImpl
clone() {
349 FileViewProvider provider
= getViewProvider().clone();
350 final Language language
= getLanguage();
351 PsiFileImpl clone
= (PsiFileImpl
)provider
.getPsi(language
);
352 assert clone
!= null:"Cannot find psi file with language:"+language
+ " from viewprovider:"+provider
+" virtual file:"+getVirtualFile();
354 copyCopyableDataTo(clone
);
356 if (getTreeElement() != null) {
357 // not set by provider in clone
358 final FileElement treeClone
= (FileElement
)calcTreeElement().clone();
359 clone
.myTreeElementPointer
= treeClone
; // should not use setTreeElement here because cloned file still have VirtualFile (SCR17963)
360 treeClone
.setPsi(clone
);
363 if (getViewProvider().isEventSystemEnabled()) {
364 clone
.myOriginalFile
= this;
366 else if (myOriginalFile
!= null) {
367 clone
.myOriginalFile
= myOriginalFile
;
373 @NotNull public String
getName() {
374 return getViewProvider().getVirtualFile().getName();
377 public PsiElement
setName(@NotNull String name
) throws IncorrectOperationException
{
380 return PsiFileImplUtil
.setName(this, name
);
383 public void checkSetName(String name
) throws IncorrectOperationException
{
384 if (!getViewProvider().isEventSystemEnabled()) return;
385 PsiFileImplUtil
.checkSetName(this, name
);
388 public boolean isWritable() {
389 return getViewProvider().getVirtualFile().isWritable() && !CacheUtil
.isCopy(this);
392 public PsiDirectory
getParent() {
393 return getContainingDirectory();
396 public PsiDirectory
getContainingDirectory() {
397 final VirtualFile parentFile
= getViewProvider().getVirtualFile().getParent();
398 if (parentFile
== null) return null;
399 return getManager().findDirectory(parentFile
);
402 public PsiFile
getContainingFile() {
406 public void delete() throws IncorrectOperationException
{
408 PsiFileImplUtil
.doDelete(this);
411 public void checkDelete() throws IncorrectOperationException
{
412 if (!getViewProvider().isEventSystemEnabled()) {
413 throw new IncorrectOperationException();
415 CheckUtil
.checkWritable(this);
418 public PsiFile
getOriginalFile() {
419 return myOriginalFile
== null ?
this : myOriginalFile
;
422 public void setOriginalFile(@NotNull final PsiFile originalFile
) {
423 myOriginalFile
= originalFile
.getOriginalFile();
427 public PsiFile
[] getPsiRoots() {
428 final FileViewProvider viewProvider
= getViewProvider();
429 final Set
<Language
> languages
= viewProvider
.getLanguages();
430 final PsiFile
[] roots
= new PsiFile
[languages
.size()];
432 for (Language language
: languages
) {
433 roots
[i
++] = viewProvider
.getPsi(language
);
438 public boolean isPhysical() {
439 // TODO[ik] remove this shit with dummy file system
440 return getViewProvider().isEventSystemEnabled();
444 public Language
getLanguage() {
445 return myElementType
.getLanguage();
449 public FileViewProvider
getViewProvider() {
450 return myViewProvider
;
453 public void setTreeElementPointer(FileElement element
) {
454 myTreeElementPointer
= element
;
457 public PsiElement
findElementAt(int offset
) {
458 return getViewProvider().findElementAt(offset
);
461 public PsiReference
findReferenceAt(int offset
) {
462 return getViewProvider().findReferenceAt(offset
);
466 public char[] textToCharArray() {
467 return CharArrayUtil
.fromSequenceStrict(getViewProvider().getContents());
471 protected <T
> T
[] findChildrenByClass(Class
<T
> aClass
) {
472 List
<T
> result
= new ArrayList
<T
>();
473 for (PsiElement child
: getChildren()) {
474 if (aClass
.isInstance(child
)) result
.add((T
)child
);
476 return result
.toArray((T
[]) Array
.newInstance(aClass
, result
.size()));
480 protected <T
> T
findChildByClass(Class
<T
> aClass
) {
481 for (PsiElement child
: getChildren()) {
482 if (aClass
.isInstance(child
)) return (T
)child
;
487 public boolean isTemplateDataFile() {
491 public PsiElement
getContext() {
492 return FileContextUtil
.getFileContext(this);
495 public void onContentReload() {
496 subtreeChanged(); // important! otherwise cached information is not released
497 if (isContentsLoaded()) {
502 public PsiFile
cacheCopy(final FileContent content
) {
503 if (isContentsLoaded()) {
508 if (content
== null) {
509 Document document
= FileDocumentManager
.getInstance().getDocument(getVirtualFile());
510 text
= document
.getCharsSequence();
513 text
= CacheUtil
.getContentText(content
);
516 FileType fileType
= getFileType();
517 final String name
= getName();
519 PsiFileFactory
.getInstance(getProject()).createFileFromText(name
, fileType
, text
, getModificationStamp(), false, false);
520 fileCopy
.putUserData(CacheUtil
.CACHE_COPY_KEY
, Boolean
.TRUE
);
522 ((PsiFileImpl
)fileCopy
).setOriginalFile(this);
528 public StubElement
getStub() {
529 StubTree stubHolder
= getStubTree();
530 return stubHolder
!= null ? stubHolder
.getRoot() : null;
534 public StubTree
getStubTree() {
535 ApplicationManager
.getApplication().assertReadAccessAllowed();
537 if (Boolean
.TRUE
.equals(getUserData(BUILDING_STUB
))) return null;
539 final StubTree derefd
= derefStub();
540 if (derefd
!= null) return derefd
;
541 if (getTreeElementNoLock() != null) return null;
543 final VirtualFile vFile
= getVirtualFile();
544 if (!(vFile
instanceof VirtualFileWithId
)) return null;
546 StubTree stubHolder
= StubTree
.readFromVFile(vFile
);
547 if (stubHolder
== null) return null;
549 synchronized (myStubLock
) {
550 if (getTreeElementNoLock() != null) return null;
552 final StubTree derefdOnLock
= derefStub();
553 if (derefdOnLock
!= null) return derefdOnLock
;
555 myStub
= new SoftReference
<StubTree
>(stubHolder
);
556 StubBase
<PsiFile
> base
= (StubBase
)stubHolder
.getRoot();
564 private StubTree
derefStub() {
565 synchronized (myStubLock
) {
566 return myStub
!= null ? myStub
.get() : null;
570 protected PsiFileImpl
cloneImpl(FileElement treeElementClone
) {
571 PsiFileImpl clone
= (PsiFileImpl
)super.clone();
572 clone
.myTreeElementPointer
= treeElementClone
; // should not use setTreeElement here because cloned file still have VirtualFile (SCR17963)
573 treeElementClone
.setPsi(clone
);
577 private void setTreeElement(ASTNode treeElement
){
579 if (treeElement
== null) {
582 else if (isKeepTreeElementByHardReference()) {
583 newPointer
= treeElement
;
586 newPointer
= myManager
.isBatchFilesProcessingMode()
587 ?
new PatchedWeakReference
<ASTNode
>(treeElement
)
588 : new PatchedSoftReference
<ASTNode
>(treeElement
);
591 synchronized (myStubLock
) {
592 myTreeElementPointer
= newPointer
;
596 public Object
getStubLock() {
600 public PsiManager
getManager() {
604 public PsiElement
getNavigationElement() {
608 public PsiElement
getOriginalElement() {
612 public final CompositeElement
calcTreeElement() {
613 // Attempt to find (loaded) tree element without taking lock first.
614 FileElement treeElement
= getTreeElement();
615 if (treeElement
!= null) return treeElement
;
617 synchronized (myStubLock
) {
618 treeElement
= getTreeElement();
619 if (treeElement
!= null) return treeElement
;
621 return loadTreeElement();
627 public PsiElement
[] getChildren() {
628 return calcTreeElement().getChildrenAsPsiElements(null, PsiElementArrayConstructor
.PSI_ELEMENT_ARRAY_CONSTRUCTOR
);
631 public PsiElement
getFirstChild() {
632 return SharedImplUtil
.getFirstChild(calcTreeElement());
635 public PsiElement
getLastChild() {
636 return SharedImplUtil
.getLastChild(calcTreeElement());
639 public void acceptChildren(@NotNull PsiElementVisitor visitor
) {
640 SharedImplUtil
.acceptChildren(visitor
, calcTreeElement());
643 public int getStartOffsetInParent() {
644 return calcTreeElement().getStartOffsetInParent();
646 public int getTextOffset() {
647 return calcTreeElement().getTextOffset();
650 public boolean textMatches(@NotNull CharSequence text
) {
651 return calcTreeElement().textMatches(text
);
654 public boolean textMatches(@NotNull PsiElement element
) {
655 return calcTreeElement().textMatches(element
);
658 public boolean textContains(char c
) {
659 return calcTreeElement().textContains(c
);
662 public final PsiElement
copy() {
666 public PsiElement
add(@NotNull PsiElement element
) throws IncorrectOperationException
{
667 CheckUtil
.checkWritable(this);
668 TreeElement elementCopy
= ChangeUtil
.copyToElement(element
);
669 calcTreeElement().addInternal(elementCopy
, elementCopy
, null, null);
670 elementCopy
= ChangeUtil
.decodeInformation(elementCopy
);
671 return SourceTreeToPsiMap
.treeElementToPsi(elementCopy
);
674 public PsiElement
addBefore(@NotNull PsiElement element
, PsiElement anchor
) throws IncorrectOperationException
{
675 CheckUtil
.checkWritable(this);
676 TreeElement elementCopy
= ChangeUtil
.copyToElement(element
);
677 calcTreeElement().addInternal(elementCopy
, elementCopy
, SourceTreeToPsiMap
.psiElementToTree(anchor
), Boolean
.TRUE
);
678 elementCopy
= ChangeUtil
.decodeInformation(elementCopy
);
679 return SourceTreeToPsiMap
.treeElementToPsi(elementCopy
);
682 public PsiElement
addAfter(@NotNull PsiElement element
, PsiElement anchor
) throws IncorrectOperationException
{
683 CheckUtil
.checkWritable(this);
684 TreeElement elementCopy
= ChangeUtil
.copyToElement(element
);
685 calcTreeElement().addInternal(elementCopy
, elementCopy
, SourceTreeToPsiMap
.psiElementToTree(anchor
), Boolean
.FALSE
);
686 elementCopy
= ChangeUtil
.decodeInformation(elementCopy
);
687 return SourceTreeToPsiMap
.treeElementToPsi(elementCopy
);
690 public final void checkAdd(@NotNull PsiElement element
) throws IncorrectOperationException
{
691 CheckUtil
.checkWritable(this);
694 public PsiElement
addRange(PsiElement first
, PsiElement last
) throws IncorrectOperationException
{
695 return SharedImplUtil
.addRange(this, first
, last
, null, null);
698 public PsiElement
addRangeBefore(@NotNull PsiElement first
, @NotNull PsiElement last
, PsiElement anchor
)
699 throws IncorrectOperationException
{
700 return SharedImplUtil
.addRange(this, first
, last
, SourceTreeToPsiMap
.psiElementToTree(anchor
), Boolean
.TRUE
);
703 public PsiElement
addRangeAfter(PsiElement first
, PsiElement last
, PsiElement anchor
)
704 throws IncorrectOperationException
{
705 return SharedImplUtil
.addRange(this, first
, last
, SourceTreeToPsiMap
.psiElementToTree(anchor
), Boolean
.FALSE
);
708 public void deleteChildRange(PsiElement first
, PsiElement last
) throws IncorrectOperationException
{
709 CheckUtil
.checkWritable(this);
711 LOG
.assertTrue(last
== null);
714 ASTNode firstElement
= SourceTreeToPsiMap
.psiElementToTree(first
);
715 ASTNode lastElement
= SourceTreeToPsiMap
.psiElementToTree(last
);
716 CompositeElement treeElement
= calcTreeElement();
717 LOG
.assertTrue(firstElement
.getTreeParent() == treeElement
);
718 LOG
.assertTrue(lastElement
.getTreeParent() == treeElement
);
719 CodeEditUtil
.removeChildren(treeElement
, firstElement
, lastElement
);
722 public PsiElement
replace(@NotNull PsiElement newElement
) throws IncorrectOperationException
{
723 CompositeElement treeElement
= calcTreeElement();
724 LOG
.assertTrue(treeElement
.getTreeParent() != null);
725 CheckUtil
.checkWritable(this);
726 TreeElement elementCopy
= ChangeUtil
.copyToElement(newElement
);
727 treeElement
.getTreeParent().replaceChildInternal(treeElement
, elementCopy
);
728 elementCopy
= ChangeUtil
.decodeInformation(elementCopy
);
729 return SourceTreeToPsiMap
.treeElementToPsi(elementCopy
);
732 public PsiReference
getReference() {
737 public PsiReference
[] getReferences() {
738 return SharedPsiElementImplUtil
.getReferences(this);
741 public boolean processDeclarations(@NotNull PsiScopeProcessor processor
,
742 @NotNull ResolveState state
,
743 PsiElement lastParent
,
744 @NotNull PsiElement place
) {
749 public GlobalSearchScope
getResolveScope() {
750 return ((PsiManagerEx
)getManager()).getFileManager().getResolveScope(this);
754 public SearchScope
getUseScope() {
755 return ((PsiManagerEx
) getManager()).getFileManager().getUseScope(this);
758 public ItemPresentation
getPresentation() {
759 return new ItemPresentation() {
760 public String
getPresentableText() {
764 public String
getLocationString() {
765 final PsiDirectory psiDirectory
= getParent();
766 if (psiDirectory
!= null) {
767 return psiDirectory
.getVirtualFile().getPresentableUrl();
772 public Icon
getIcon(final boolean open
) {
773 return PsiFileImpl
.this.getIcon(open ? ICON_FLAG_OPEN
: ICON_FLAG_CLOSED
);
776 public TextAttributesKey
getTextAttributesKey() {
782 public void navigate(boolean requestFocus
) {
783 EditSourceUtil
.getDescriptor(this).navigate(requestFocus
);
786 public boolean canNavigate() {
787 return EditSourceUtil
.canNavigate(this);
790 public boolean canNavigateToSource() {
791 return canNavigate();
794 public FileStatus
getFileStatus() {
795 if (!isPhysical()) return FileStatus
.NOT_CHANGED
;
796 PsiFile contFile
= getContainingFile();
797 if (contFile
== null) return FileStatus
.NOT_CHANGED
;
798 VirtualFile vFile
= contFile
.getVirtualFile();
799 return vFile
!= null ? FileStatusManager
.getInstance(getProject()).getStatus(vFile
) : FileStatus
.NOT_CHANGED
;
803 public Project
getProject() {
804 final PsiManager manager
= getManager();
805 if (manager
== null) throw new PsiInvalidElementAccessException(this);
807 return manager
.getProject();
810 public ASTNode
getNode() {
811 return calcTreeElement();
814 public boolean isEquivalentTo(final PsiElement another
) {
815 return this == another
;
818 private static final Key
<StubTree
> STUB_TREE_IN_PARSED_TREE
= new Key
<StubTree
>("STUB_TREE_IN_PARSED_TREE");
820 public StubTree
calcStubTree() {
821 synchronized (myStubLock
) {
822 final FileElement fileElement
= (FileElement
)calcTreeElement();
823 StubTree tree
= fileElement
.getUserData(STUB_TREE_IN_PARSED_TREE
);
825 final StubElement currentStubTree
= ((IStubFileElementType
)getContentElementType()).getBuilder().buildStubTree(this);
826 tree
= new StubTree((PsiFileStub
)currentStubTree
);
827 bindFakeStubsToTree(tree
);
828 fileElement
.putUserData(STUB_TREE_IN_PARSED_TREE
, tree
);
834 private void bindFakeStubsToTree(StubTree stubTree
) {
835 final PsiFileImpl file
= this;
837 final Iterator
<StubElement
<?
>> stubs
= stubTree
.getPlainList().iterator();
838 stubs
.next(); // skip file root stub
839 final FileElement fileRoot
= file
.getTreeElement();
840 assert fileRoot
!= null;
842 bindStubs(fileRoot
, stubs
);
846 private StubElement
bindStubs(ASTNode tree
, Iterator
<StubElement
<?
>> stubs
) {
847 if (tree
instanceof ChameleonElement
) {
848 tree
= ChameleonTransforming
.transform((ChameleonElement
)tree
);
851 final IElementType type
= tree
.getElementType();
853 if (type
instanceof IStubElementType
&& ((IStubElementType
) type
).shouldCreateStub(tree
)) {
854 final StubElement stub
= stubs
.next();
855 if (stub
.getStubType() != tree
.getElementType()) {
858 assert false: "Stub and PSI element type mismatch: stub " + stub
+ ", AST " + tree
.getElementType();
861 ((StubBase
)stub
).setPsi(tree
.getPsi());
864 for (ASTNode node
: tree
.getChildren(null)) {
865 final StubElement res
= bindStubs(node
, stubs
);
874 private void rebuildStub() {
875 final VirtualFile vFile
= getVirtualFile();
877 if (vFile
!= null && vFile
.isValid()) {
878 ApplicationManager
.getApplication().invokeLater(new Runnable() {
880 final Document doc
= FileDocumentManager
.getInstance().getCachedDocument(vFile
);
882 FileDocumentManager
.getInstance().saveDocument(doc
);
885 }, ModalityState
.NON_MODAL
);
887 FileBasedIndex
.getInstance().requestReindex(vFile
);