blob: 12c94d2a429911127b4265dbecdc7a066c85a0f7 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.smartPointers;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.CodeInsightTestCase;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.editor.event.EditorEventMulticaster;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.StubTree;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.PlatformTestCase;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.util.FileContentUtil;
import gnu.trove.THashSet;
import org.junit.Assert;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
@PlatformTestCase.WrapInCommand
public class SmartPsiElementPointersTest extends CodeInsightTestCase {
private VirtualFile myRoot;
@Override
protected void setUp() throws Exception {
super.setUp();
String root = JavaTestUtil.getJavaTestDataPath() + "/codeEditor/smartPsiElementPointers";
PsiTestUtil.removeAllRoots(myModule, IdeaTestUtil.getMockJdk17());
myRoot = PsiTestUtil.createTestProjectStructure(myProject, myModule, root, myFilesToDelete);
}
public void testChangeInDocument() {
PsiClass aClass = myJavaFacade.findClass("AClass", GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
SmartPsiElementPointer pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
Document document = PsiDocumentManager.getInstance(myProject).getDocument(aClass.getContainingFile());
int offset = aClass.getTextOffset();
document.insertString(offset, "/**/");
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiElement element = pointer.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiClass);
assertTrue(element.isValid());
}
// This test is unfair. If pointer would be asked for getElement() between commits it'll never restore again anyway.
//
public void testChangeInDocumentTwice() {
PsiClass aClass = myJavaFacade.findClass("AClass",GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
SmartPsiElementPointer pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
Document document = PsiDocumentManager.getInstance(myProject).getDocument(aClass.getContainingFile());
int offset = aClass.getTextOffset();
document.insertString(offset, "/*");
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
document.insertString(offset + 2, "*/");
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiElement element = pointer.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiClass);
assertTrue(element.isValid());
}
public void testGetElementWhenDocumentModified() {
PsiClass aClass = myJavaFacade.findClass("AClass",GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
SmartPsiElementPointer pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
Document document = PsiDocumentManager.getInstance(myProject).getDocument(aClass.getContainingFile());
int offset = aClass.getTextOffset();
document.insertString(offset, "/**/");
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
document.insertString(offset, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
PsiElement element = pointer.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiClass);
assertTrue(element.isValid());
}
public void testKeepBeltWhenDocumentModified() {
PsiClass aClass = myJavaFacade.findClass("AClass",GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
SmartPsiElementPointer pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
Document document = PsiDocumentManager.getInstance(myProject).getDocument(aClass.getContainingFile());
int offset = aClass.getTextOffset();
document.insertString(offset, "/******/");
pointer.getElement();
document.insertString(offset, "/**/");
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiElement element = pointer.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiClass);
assertTrue(element.isValid());
}
public void testChangeInPsi() {
PsiClass aClass = myJavaFacade.findClass("AClass",GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
SmartPsiElementPointer pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
Document document = PsiDocumentManager.getInstance(myProject).getDocument(aClass.getContainingFile());
int offset = aClass.getTextOffset();
document.insertString(offset, "/**/");
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiElement element = pointer.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiClass);
assertTrue(element.isValid());
}
public void testPsiChangesWithLazyPointers() throws Exception {
PsiClass aClass = myJavaFacade.findClass("AClass",GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
final SmartPsiElementPointer<PsiIdentifier> pointer =
SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass.getNameIdentifier());
final PsiComment javadoc =
JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createCommentFromText("/** javadoc */", aClass);
aClass.getParent().addBefore(javadoc, aClass);
final PsiIdentifier elt = pointer.getElement();
assertNotNull(elt);
assertSame(elt, aClass.getNameIdentifier());
}
public void testTypePointer() {
PsiClass aClass = myJavaFacade.findClass("AClass",GlobalSearchScope.allScope(getProject()));
final PsiTypeElement typeElement = myJavaFacade.findClass("Test",GlobalSearchScope.allScope(getProject())).getFields()[0].getTypeElement();
SmartPsiElementPointer typePointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(typeElement);
SmartPsiElementPointer classPointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
Document aClassDocument = PsiDocumentManager.getInstance(myProject).getDocument(aClass.getContainingFile());
Document testDocument = PsiDocumentManager.getInstance(myProject).getDocument(typeElement.getContainingFile());
assertNotSame(aClassDocument, testDocument);
aClassDocument.insertString(aClass.getTextOffset(), "/**/");
testDocument.insertString(typeElement.getTextOffset(), "/**/");
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiElement element = typePointer.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiTypeElement);
assertTrue(element.isValid());
assertEquals(classPointer.getElement(), PsiUtil.resolveClassInType(((PsiTypeElement)element).getType()));
}
public void testCreatePointerInBeforeDocumentChange() {
final PsiClass aClass = myJavaFacade.findClass("AClass",GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
Document document = PsiDocumentManager.getInstance(myProject).getDocument(aClass.getContainingFile());
final SmartPsiElementPointer[] pointer = new SmartPsiElementPointer[1];
int offset = aClass.getTextOffset();
DocumentListener listener = new DocumentListener() {
@Override
public void beforeDocumentChange(DocumentEvent event) {
pointer[0] = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
}
@Override
public void documentChanged(DocumentEvent event) {
}
};
EditorEventMulticaster multicaster = EditorFactory.getInstance().getEventMulticaster();
multicaster.addDocumentListener(listener);
try {
document.insertString(offset, "/******/");
}
finally {
multicaster.removeDocumentListener(listener);
}
pointer[0].getElement();
document.insertString(0, "/**/");
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
PsiElement element = pointer[0].getElement();
assertNotNull(element);
assertTrue(element instanceof PsiClass);
assertTrue(element.isValid());
}
public void testCreatePointerWhenNoPsiFile() {
myPsiManager.startBatchFilesProcessingMode(); // to use weak refs
try {
final PsiClass aClass = myJavaFacade.findClass("AClass",GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
VirtualFile vFile = myRoot.findChild("AClass.java");
assertNotNull(vFile);
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(myProject);
Document document = FileDocumentManager.getInstance().getDocument(vFile);
final SmartPsiElementPointer pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
System.gc();
/*
PsiFile psiFile = myPsiManager.getFileManager().getCachedPsiFile(vFile);
assertNull(psiFile);
*/
document.insertString(0, "class Foo{}\n");
PsiElement element = pointer.getElement();
assertEquals(aClass, element);
document.insertString(0, "/**/");
psiDocumentManager.commitAllDocuments();
if (aClass.isValid()) {
aClass.getChildren();
}
element = pointer.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiClass);
assertTrue(element.isValid());
}
finally {
myPsiManager.finishBatchFilesProcessingMode(); // to use weak refs
}
}
public void testReplaceFile() throws IOException {
VirtualFile vfile = myRoot.createChildData(this, "X.java");
VfsUtil.saveText(vfile, "public class X { public int X; }");
PsiClass aClass = myJavaFacade.findClass("X", GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
assertTrue(aClass.isValid());
SmartPsiElementPointer classp = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
SmartPsiElementPointer filep = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass.getContainingFile());
FileContentUtil.reparseFiles(myProject, Collections.<VirtualFile>singleton(vfile), true);
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
assertFalse(aClass.isValid());
PsiElement element = classp.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiClass);
assertTrue(element.isValid());
assertEquals(vfile, element.getContainingFile().getVirtualFile());
element = filep.getElement();
assertNotNull(element);
assertTrue(element instanceof PsiFile);
assertTrue(element.isValid());
assertEquals(vfile, element.getContainingFile().getVirtualFile());
}
public void testCreatePointerDoesNotLoadPsiTree() throws IOException {
VirtualFile vfile = myRoot.createChildData(this, "X.java");
VfsUtil.saveText(vfile, "public class X { public int X; }");
PsiClass aClass = myJavaFacade.findClass("X", GlobalSearchScope.allScope(getProject()));
assertNotNull(aClass);
assertTrue(aClass.isValid());
PsiFileImpl file = (PsiFileImpl)aClass.getContainingFile();
assertTreeLoaded(file, false);
SmartPsiElementPointer p = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(aClass);
assertNotNull(p);
assertTreeLoaded(file, false);
assertInstanceOf(p.getElement(), PsiClass.class);
assertTreeLoaded(file, false);
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
Document document = documentManager.getDocument(file);
document.insertString(0, "/** asdasd */");
documentManager.commitAllDocuments();
// loaded tree
assertTreeLoaded(file, true);
assertInstanceOf(p.getElement(), PsiClass.class);
assertTreeLoaded(file, true);
}
private static void assertTreeLoaded(PsiFileImpl file, boolean loaded) {
FileElement treeElement = file.getTreeElement();
assertEquals(loaded, treeElement != null);
StubTree stubTree = file.getStubTree();
assertEquals(loaded, stubTree == null);
}
public void testPointerDisambiguationAfterDupLine() throws Exception {
PsiJavaFile file = (PsiJavaFile)configureByText(StdFileTypes.JAVA, "class XXX{ void foo() { \n" +
" <caret>foo();\n" +
"}}");
PsiClass aClass = file.getClasses()[0];
assertNotNull(aClass);
PsiReferenceExpression ref1 = PsiTreeUtil.getParentOfType(PsiUtilBase.getElementAtCaret(getEditor()), PsiReferenceExpression.class);
SmartPsiElementPointer pointer1 = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(ref1);
ctrlD();
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
Set<PsiReferenceExpression> refs = new THashSet<PsiReferenceExpression>();
int offset=0;
while (true) {
offset = getEditor().getDocument().getText().indexOf("foo();", offset+1);
if (offset == -1) break;
PsiReferenceExpression ref2 = PsiTreeUtil.getParentOfType(getFile().findElementAt(offset), PsiReferenceExpression.class);
refs.add(ref2);
}
refs.remove(ref1);
assertEquals(1, refs.size());
PsiReferenceExpression ref2 = refs.iterator().next();
assertNotSame(ref1, ref2);
SmartPsiElementPointer pointer2 = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(ref2);
assertNotSame(pointer1, pointer2);
PsiElement element1 = pointer1.getElement();
PsiElement element2 = pointer2.getElement();
assertNotNull(element1);
assertNotNull(element2);
assertNotSame(element1, element2);
assertFalse(SmartPointerManager.getInstance(myProject).pointToTheSameElement(pointer1, pointer2));
}
public void testPointersRefCount() throws Exception {
PsiFile file = configureByText(JavaFileType.INSTANCE, "class X{}");
PsiClass aClass = ((PsiClassOwner)file).getClasses()[0];
SmartPointerManagerImpl smartPointerManager = (SmartPointerManagerImpl)SmartPointerManager.getInstance(myProject);
SmartPsiElementPointer pointer1 = smartPointerManager.createSmartPsiElementPointer(aClass);
SmartPsiElementPointer pointer2 = smartPointerManager.createSmartPsiElementPointer(aClass);
assertSame(pointer1, pointer2);
assertNotNull(pointer1.getRange());
boolean removed2 = smartPointerManager.removePointer(pointer2);
assertFalse(removed2);
assertNotNull(pointer1.getRange());
boolean removed1 = smartPointerManager.removePointer(pointer1);
assertTrue(removed1);
assertNull(pointer1.getRange());
}
public void testPointersRefCountSaturated() throws Exception {
PsiFile file = configureByText(JavaFileType.INSTANCE, "class X{}");
PsiClass aClass = ((PsiClassOwner)file).getClasses()[0];
SmartPointerManagerImpl smartPointerManager = (SmartPointerManagerImpl)SmartPointerManager.getInstance(myProject);
SmartPsiElementPointerImpl pointer1 = (SmartPsiElementPointerImpl)smartPointerManager.createSmartPsiElementPointer(aClass);
for (int i=0; i<1000; i++) {
SmartPsiElementPointer<PsiClass> pointer2 = smartPointerManager.createSmartPsiElementPointer(aClass);
assertSame(pointer1, pointer2);
}
assertNotNull(pointer1.getRange());
assertEquals(Byte.MAX_VALUE, pointer1.incrementAndGetReferenceCount(0));
for (int i=0; i<1100; i++) {
boolean removed1 = smartPointerManager.removePointer(pointer1);
assertFalse(removed1);
Assert.assertNotNull(pointer1.getRange());
}
}
public void testSmartPointerCreationDoesNotLoadDocument() {
PsiPackage aPackage = myJavaFacade.findPackage("java.io");
SmartPointerManagerImpl smartPointerManager = (SmartPointerManagerImpl)SmartPointerManager.getInstance(myProject);
for (PsiClass aClass : aPackage.getClasses()) {
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
PsiFile file = aClass.getContainingFile();
Document document = documentManager.getCachedDocument(file);
if (document == null) { //ignore already loaded documents
SmartPsiElementPointer pointer = smartPointerManager.createSmartPsiElementPointer(aClass);
assertNull(documentManager.getCachedDocument(file));
//System.out.println("file = " + file);
}
else {
System.out.println("already loaded file = " + file);
}
}
}
}