Don't pass five parameters to LeafElement's constuctor to initialize single myText...
[fedora-idea.git] / lang-impl / src / com / intellij / psi / impl / source / PsiFileImpl.java
blob1dcf1a27d073694bfc0c5ad9bf850a35f607f87f
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;
48 import javax.swing.*;
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;
54 import java.util.Set;
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) {
72 this(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() {
99 return false;
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;
137 return null;
142 public VirtualFile getVirtualFile() {
143 return getViewProvider().isEventSystemEnabled() ? getViewProvider().getVirtualFile() : null;
146 public boolean processChildren(final PsiElementProcessor<PsiFileSystemItem> processor) {
147 return true;
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();
161 //return valid;
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();
195 if (stub != null) {
196 final Iterator<StubElement<?>> stubs = stub.getPlainList().iterator();
197 stubs.next(); // Skip file stub;
198 switchFromStubToAST(treeElement, stubs);
200 myStub = null;
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());
210 return treeElement;
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;
240 return null;
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()) {
254 rebuildStub();
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;
260 base.setNode(tree);
261 base.setStub(null);
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)) {
276 LOG.error("BUMM!");
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);
285 return treeElement;
288 public void unloadContent() {
289 LOG.assertTrue(getTreeElement() != null);
290 clearCaches();
291 myViewProvider.beforeContentsSynchronized();
292 setTreeElement(null);
293 synchronized (myStubLock) {
294 myStub = null;
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() {
328 doClearCaches();
329 getViewProvider().rootChanged(this);
332 private void doClearCaches() {
333 final FileElement tree = getTreeElement();
334 if (tree != null) {
335 myTreeElementPointer = tree;
336 tree.clearCaches();
337 tree.putUserData(STUB_TREE_IN_PARSED_TREE, null);
340 synchronized (myStubLock) {
341 myStub = null;
344 clearCaches();
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;
370 return clone;
373 @NotNull public String getName() {
374 return getViewProvider().getVirtualFile().getName();
377 public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
378 checkSetName(name);
379 doClearCaches();
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() {
403 return this;
406 public void delete() throws IncorrectOperationException {
407 checkDelete();
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();
426 @NotNull
427 public PsiFile[] getPsiRoots() {
428 final FileViewProvider viewProvider = getViewProvider();
429 final Set<Language> languages = viewProvider.getLanguages();
430 final PsiFile[] roots = new PsiFile[languages.size()];
431 int i = 0;
432 for (Language language : languages) {
433 roots[i++] = viewProvider.getPsi(language);
435 return roots;
438 public boolean isPhysical() {
439 // TODO[ik] remove this shit with dummy file system
440 return getViewProvider().isEventSystemEnabled();
443 @NotNull
444 public Language getLanguage() {
445 return myElementType.getLanguage();
448 @NotNull
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);
465 @NotNull
466 public char[] textToCharArray() {
467 return CharArrayUtil.fromSequenceStrict(getViewProvider().getContents());
470 @NotNull
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()));
479 @Nullable
480 protected <T> T findChildByClass(Class<T> aClass) {
481 for (PsiElement child : getChildren()) {
482 if (aClass.isInstance(child)) return (T)child;
484 return null;
487 public boolean isTemplateDataFile() {
488 return false;
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()) {
498 unloadContent();
502 public PsiFile cacheCopy(final FileContent content) {
503 if (isContentsLoaded()) {
504 return this;
506 else {
507 CharSequence text;
508 if (content == null) {
509 Document document = FileDocumentManager.getInstance().getDocument(getVirtualFile());
510 text = document.getCharsSequence();
512 else {
513 text = CacheUtil.getContentText(content);
516 FileType fileType = getFileType();
517 final String name = getName();
518 PsiFile fileCopy =
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);
523 return fileCopy;
527 @Nullable
528 public StubElement getStub() {
529 StubTree stubHolder = getStubTree();
530 return stubHolder != null ? stubHolder.getRoot() : null;
533 @Nullable
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();
557 base.setPsi(this);
559 return derefStub();
563 @Nullable
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);
574 return clone;
577 private void setTreeElement(ASTNode treeElement){
578 Object newPointer;
579 if (treeElement == null) {
580 newPointer = null;
582 else if (isKeepTreeElementByHardReference()) {
583 newPointer = treeElement;
585 else {
586 newPointer = myManager.isBatchFilesProcessingMode()
587 ? new PatchedWeakReference<ASTNode>(treeElement)
588 : new PatchedSoftReference<ASTNode>(treeElement);
591 synchronized (myStubLock) {
592 myTreeElementPointer = newPointer;
596 public Object getStubLock() {
597 return myStubLock;
600 public PsiManager getManager() {
601 return myManager;
604 public PsiElement getNavigationElement() {
605 return this;
608 public PsiElement getOriginalElement() {
609 return this;
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();
626 @NotNull
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() {
663 return clone();
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);
710 if (first == null) {
711 LOG.assertTrue(last == null);
712 return;
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() {
733 return null;
736 @NotNull
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) {
745 return true;
748 @NotNull
749 public GlobalSearchScope getResolveScope() {
750 return ((PsiManagerEx)getManager()).getFileManager().getResolveScope(this);
753 @NotNull
754 public SearchScope getUseScope() {
755 return ((PsiManagerEx) getManager()).getFileManager().getUseScope(this);
758 public ItemPresentation getPresentation() {
759 return new ItemPresentation() {
760 public String getPresentableText() {
761 return getName();
764 public String getLocationString() {
765 final PsiDirectory psiDirectory = getParent();
766 if (psiDirectory != null) {
767 return psiDirectory.getVirtualFile().getPresentableUrl();
769 return null;
772 public Icon getIcon(final boolean open) {
773 return PsiFileImpl.this.getIcon(open ? ICON_FLAG_OPEN : ICON_FLAG_CLOSED);
776 public TextAttributesKey getTextAttributesKey() {
777 return null;
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;
802 @NotNull
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);
824 if (tree == null) {
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);
830 return 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);
845 @Nullable
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()) {
856 rebuildStub();
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);
866 if (res != null) {
867 return res;
871 return null;
874 private void rebuildStub() {
875 final VirtualFile vFile = getVirtualFile();
877 if (vFile != null && vFile.isValid()) {
878 ApplicationManager.getApplication().invokeLater(new Runnable() {
879 public void run() {
880 final Document doc = FileDocumentManager.getInstance().getCachedDocument(vFile);
881 if (doc != null) {
882 FileDocumentManager.getInstance().saveDocument(doc);
885 }, ModalityState.NON_MODAL);
887 FileBasedIndex.getInstance().requestReindex(vFile);