mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 13:39:36 +07:00
420 lines
18 KiB
Java
420 lines
18 KiB
Java
/*
|
|
* Copyright 2000-2013 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.
|
|
*/
|
|
|
|
/*
|
|
* User: anna
|
|
* Date: 27-Jun-2007
|
|
*/
|
|
package com.intellij.codeInsight;
|
|
|
|
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
|
|
import com.intellij.codeInsight.intention.IntentionAction;
|
|
import com.intellij.codeInsight.intention.impl.DeannotateIntentionAction;
|
|
import com.intellij.openapi.application.ApplicationManager;
|
|
import com.intellij.openapi.application.Result;
|
|
import com.intellij.openapi.application.ex.PathManagerEx;
|
|
import com.intellij.openapi.command.WriteCommandAction;
|
|
import com.intellij.openapi.editor.CaretModel;
|
|
import com.intellij.openapi.editor.Editor;
|
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
|
import com.intellij.openapi.module.Module;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.roots.AnnotationOrderRootType;
|
|
import com.intellij.openapi.roots.ModifiableRootModel;
|
|
import com.intellij.openapi.roots.ModuleRootManager;
|
|
import com.intellij.openapi.roots.OrderRootType;
|
|
import com.intellij.openapi.roots.libraries.Library;
|
|
import com.intellij.openapi.roots.libraries.LibraryTable;
|
|
import com.intellij.openapi.util.Trinity;
|
|
import com.intellij.openapi.util.io.FileUtil;
|
|
import com.intellij.openapi.util.io.StreamUtil;
|
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
|
import com.intellij.openapi.vfs.VfsUtil;
|
|
import com.intellij.openapi.vfs.VirtualFile;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.psi.util.PsiTreeUtil;
|
|
import com.intellij.testFramework.IdeaTestCase;
|
|
import com.intellij.testFramework.PsiTestUtil;
|
|
import com.intellij.testFramework.UsefulTestCase;
|
|
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
|
|
import com.intellij.testFramework.fixtures.*;
|
|
import com.intellij.util.messages.MessageBusConnection;
|
|
import org.jetbrains.annotations.NonNls;
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
public class AddAnnotationFixTest extends UsefulTestCase {
|
|
private CodeInsightTestFixture myFixture;
|
|
private Module myModule;
|
|
private Project myProject;
|
|
private boolean myExpectedEventWasProduced = false;
|
|
private boolean myUnexpectedEventWasProduced = false;
|
|
private MessageBusConnection myBusConnection = null;
|
|
|
|
public AddAnnotationFixTest() {
|
|
IdeaTestCase.initPlatformPrefix();
|
|
}
|
|
|
|
@Override
|
|
public void setUp() throws Exception {
|
|
super.setUp();
|
|
final TestFixtureBuilder<IdeaProjectTestFixture> projectBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName());
|
|
|
|
myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(projectBuilder.getFixture());
|
|
final String dataPath = PathManagerEx.getTestDataPath() + "/codeInsight/externalAnnotations";
|
|
myFixture.setTestDataPath(dataPath);
|
|
final JavaModuleFixtureBuilder builder = projectBuilder.addModule(JavaModuleFixtureBuilder.class);
|
|
builder.setMockJdkLevel(JavaModuleFixtureBuilder.MockJdkLevel.jdk15);
|
|
|
|
myFixture.setUp();
|
|
myModule = builder.getFixture().getModule();
|
|
myProject = myFixture.getProject();
|
|
}
|
|
|
|
@Override
|
|
protected void tearDown() throws Exception {
|
|
super.tearDown();
|
|
myFixture.tearDown();
|
|
myFixture = null;
|
|
myModule = null;
|
|
myProject = null;
|
|
assertNull(myBusConnection);
|
|
}
|
|
|
|
private void addDefaultLibrary() {
|
|
addLibrary("/content/anno");
|
|
}
|
|
|
|
private void addLibrary(@NotNull final String... annotationsDirs) {
|
|
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
final ModifiableRootModel model = ModuleRootManager.getInstance(myModule).getModifiableModel();
|
|
final LibraryTable libraryTable = model.getModuleLibraryTable();
|
|
final Library library = libraryTable.createLibrary("test");
|
|
|
|
final Library.ModifiableModel libraryModel = library.getModifiableModel();
|
|
libraryModel.addRoot(VfsUtil.pathToUrl(myFixture.getTempDirPath() + "/lib"), OrderRootType.SOURCES);
|
|
for (String annotationsDir : annotationsDirs) {
|
|
libraryModel.addRoot(VfsUtil.pathToUrl(myFixture.getTempDirPath() + annotationsDir), AnnotationOrderRootType.getInstance());
|
|
}
|
|
libraryModel.commit();
|
|
model.commit();
|
|
}
|
|
});
|
|
}
|
|
|
|
@NotNull
|
|
private PsiModifierListOwner getOwner() {
|
|
CaretModel caretModel = myFixture.getEditor().getCaretModel();
|
|
int position = caretModel.getOffset();
|
|
PsiElement element = myFixture.getFile().findElementAt(position);
|
|
assert element != null;
|
|
PsiModifierListOwner container = AddAnnotationPsiFix.getContainer(element);
|
|
assert container != null;
|
|
return container;
|
|
}
|
|
|
|
private void startListening(@NotNull final List<Trinity<PsiModifierListOwner, String, Boolean>> expectedSequence) {
|
|
myBusConnection = myProject.getMessageBus().connect();
|
|
myBusConnection.subscribe(ExternalAnnotationsManager.TOPIC, new DefaultAnnotationsListener() {
|
|
private int index = 0;
|
|
|
|
@Override
|
|
public void afterExternalAnnotationChanging(@NotNull PsiModifierListOwner owner, @NotNull String annotationFQName,
|
|
boolean successful) {
|
|
if (index < expectedSequence.size() && expectedSequence.get(index).first == owner
|
|
&& expectedSequence.get(index).second.equals(annotationFQName) && expectedSequence.get(index).third == successful) {
|
|
index++;
|
|
myExpectedEventWasProduced = true;
|
|
}
|
|
else {
|
|
super.afterExternalAnnotationChanging(owner, annotationFQName, successful);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void startListening(@NotNull final PsiModifierListOwner expectedOwner, @NotNull final String expectedAnnotationFQName,
|
|
final boolean expectedSuccessful) {
|
|
startListening(Arrays.asList(Trinity.create(expectedOwner, expectedAnnotationFQName, expectedSuccessful)));
|
|
}
|
|
|
|
private void startListeningForExternalChanges() {
|
|
myBusConnection = myProject.getMessageBus().connect();
|
|
myBusConnection.subscribe(ExternalAnnotationsManager.TOPIC, new DefaultAnnotationsListener() {
|
|
private boolean notifiedOnce;
|
|
|
|
@Override
|
|
public void externalAnnotationsChangedExternally() {
|
|
if (!notifiedOnce) {
|
|
myExpectedEventWasProduced = true;
|
|
notifiedOnce = true;
|
|
}
|
|
else {
|
|
super.externalAnnotationsChangedExternally();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void stopListeningAndCheckEvents() {
|
|
myBusConnection.disconnect();
|
|
myBusConnection = null;
|
|
|
|
assertTrue(myExpectedEventWasProduced);
|
|
assertFalse(myUnexpectedEventWasProduced);
|
|
|
|
myExpectedEventWasProduced = false;
|
|
myUnexpectedEventWasProduced = false;
|
|
}
|
|
|
|
public void testAnnotateLibrary() throws Throwable {
|
|
|
|
addDefaultLibrary();
|
|
myFixture.configureByFiles("lib/p/TestPrimitive.java", "content/anno/p/annotations.xml");
|
|
myFixture.configureByFiles("lib/p/Test.java");
|
|
final PsiFile file = myFixture.getFile();
|
|
final Editor editor = myFixture.getEditor();
|
|
|
|
final IntentionAction fix = myFixture.findSingleIntention("Annotate method 'get' as @NotNull");
|
|
assertTrue(fix.isAvailable(myProject, editor, file));
|
|
|
|
// expecting other @Nullable annotations to be removed, and default @NotNull to be added
|
|
List<Trinity<PsiModifierListOwner, String, Boolean>> expectedSequence
|
|
= new ArrayList<Trinity<PsiModifierListOwner, String, Boolean>>();
|
|
for (String notNull : NullableNotNullManager.getInstance(myProject).getNullables()) {
|
|
expectedSequence.add(Trinity.create(getOwner(), notNull, false));
|
|
}
|
|
expectedSequence.add(Trinity.create(getOwner(), AnnotationUtil.NOT_NULL, true));
|
|
startListening(expectedSequence);
|
|
new WriteCommandAction(myProject){
|
|
@Override
|
|
protected void run(final Result result) throws Throwable {
|
|
fix.invoke(myProject, editor, file);
|
|
}
|
|
}.execute();
|
|
|
|
FileDocumentManager.getInstance().saveAllDocuments();
|
|
|
|
final PsiElement psiElement = file.findElementAt(editor.getCaretModel().getOffset());
|
|
assertNotNull(psiElement);
|
|
final PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(psiElement, PsiModifierListOwner.class);
|
|
assertNotNull(listOwner);
|
|
assertNotNull(ExternalAnnotationsManager.getInstance(myProject).findExternalAnnotation(listOwner, AnnotationUtil.NOT_NULL));
|
|
stopListeningAndCheckEvents();
|
|
|
|
myFixture.checkResultByFile("content/anno/p/annotations.xml", "content/anno/p/annotationsAnnotateLibrary_after.xml", false);
|
|
}
|
|
|
|
public void testPrimitive() throws Throwable {
|
|
PsiFile psiFile = myFixture.configureByFile("lib/p/TestPrimitive.java");
|
|
PsiTestUtil.addSourceRoot(myModule, psiFile.getVirtualFile().getParent());
|
|
|
|
assertNotAvailable("Annotate method 'get' as @NotNull");
|
|
}
|
|
|
|
private void assertNotAvailable(String hint) {
|
|
List<IntentionAction> actions = myFixture.filterAvailableIntentions(hint);
|
|
assertEmpty(actions);
|
|
}
|
|
|
|
public void testAnnotated() throws Throwable {
|
|
PsiFile psiFile = myFixture.configureByFile("lib/p/TestAnnotated.java");
|
|
PsiTestUtil.addSourceRoot(myModule, psiFile.getVirtualFile().getParent());
|
|
final PsiFile file = myFixture.getFile();
|
|
final Editor editor = myFixture.getEditor();
|
|
assertNotAvailable("Annotate method 'get' as @NotNull");
|
|
assertNotAvailable("Annotate method 'get' as @Nullable");
|
|
|
|
final DeannotateIntentionAction deannotateFix = new DeannotateIntentionAction();
|
|
assertFalse(deannotateFix.isAvailable(myProject, editor, file));
|
|
}
|
|
|
|
public void testDeannotation() throws Throwable {
|
|
addDefaultLibrary();
|
|
myFixture.configureByFiles("lib/p/TestPrimitive.java", "content/anno/p/annotations.xml");
|
|
doDeannotate("lib/p/TestDeannotation.java", "Annotate method 'get' as @NotNull", "Annotate method 'get' as @Nullable");
|
|
myFixture.checkResultByFile("content/anno/p/annotations.xml", "content/anno/p/annotationsDeannotation_after.xml", false);
|
|
}
|
|
|
|
public void testDeannotation1() throws Throwable {
|
|
addDefaultLibrary();
|
|
myFixture.configureByFiles("lib/p/TestPrimitive.java", "content/anno/p/annotations.xml");
|
|
doDeannotate("lib/p/TestDeannotation1.java", "Annotate parameter 'ss' as @NotNull", "Annotate parameter 'ss' as @Nullable");
|
|
myFixture.checkResultByFile("content/anno/p/annotations.xml", "content/anno/p/annotationsDeannotation1_after.xml", false);
|
|
}
|
|
|
|
private void doDeannotate(@NonNls final String testPath, String hint1, String hint2) throws Throwable {
|
|
myFixture.configureByFile(testPath);
|
|
final PsiFile file = myFixture.getFile();
|
|
final Editor editor = myFixture.getEditor();
|
|
|
|
assertNotAvailable(hint1);
|
|
assertNotAvailable(hint2);
|
|
|
|
final DeannotateIntentionAction deannotateFix = new DeannotateIntentionAction();
|
|
assertTrue(deannotateFix.isAvailable(myProject, editor, file));
|
|
|
|
final PsiModifierListOwner container = DeannotateIntentionAction.getContainer(editor, file);
|
|
startListening(container, AnnotationUtil.NOT_NULL, true);
|
|
new WriteCommandAction(myProject){
|
|
@Override
|
|
protected void run(final Result result) throws Throwable {
|
|
ExternalAnnotationsManager.getInstance(myProject).deannotate(container, AnnotationUtil.NOT_NULL);
|
|
}
|
|
}.execute();
|
|
stopListeningAndCheckEvents();
|
|
|
|
FileDocumentManager.getInstance().saveAllDocuments();
|
|
|
|
IntentionAction fix = myFixture.findSingleIntention(hint1);
|
|
assertNotNull(fix);
|
|
|
|
fix = myFixture.findSingleIntention(hint2);
|
|
assertNotNull(fix);
|
|
|
|
assertFalse(deannotateFix.isAvailable(myProject, editor, file));
|
|
}
|
|
|
|
private static void assertMethodAndParameterAnnotationsValues(ExternalAnnotationsManager manager,
|
|
PsiMethod method,
|
|
PsiParameter parameter,
|
|
String expectedValue) {
|
|
PsiAnnotation methodAnnotation = manager.findExternalAnnotation(method, AnnotationUtil.NULLABLE);
|
|
assertNotNull(methodAnnotation);
|
|
assertEquals(expectedValue, methodAnnotation.findAttributeValue("value").getText());
|
|
|
|
PsiAnnotation parameterAnnotation = manager.findExternalAnnotation(parameter, AnnotationUtil.NOT_NULL);
|
|
assertNotNull(parameterAnnotation);
|
|
assertEquals(expectedValue, parameterAnnotation.findAttributeValue("value").getText());
|
|
}
|
|
|
|
public void testEditingMultiRootAnnotations() {
|
|
addLibrary("/content/annoMultiRoot/root1", "/content/annoMultiRoot/root2");
|
|
myFixture.configureByFiles("/content/annoMultiRoot/root1/multiRoot/annotations.xml",
|
|
"/content/annoMultiRoot/root2/multiRoot/annotations.xml");
|
|
myFixture.configureByFiles("lib/multiRoot/Test.java");
|
|
|
|
final ExternalAnnotationsManager manager = ExternalAnnotationsManager.getInstance(myProject);
|
|
final PsiMethod method = ((PsiJavaFile)myFixture.getFile()).getClasses()[0].getMethods()[0];
|
|
final PsiParameter parameter = method.getParameterList().getParameters()[0];
|
|
|
|
assertMethodAndParameterAnnotationsValues(manager, method, parameter, "\"foo\"");
|
|
|
|
final PsiAnnotation annotationFromText =
|
|
JavaPsiFacade.getElementFactory(myProject).createAnnotationFromText("@Annotation(value=\"bar\")", null);
|
|
|
|
startListening(method, AnnotationUtil.NULLABLE, true);
|
|
new WriteCommandAction(myProject) {
|
|
@Override
|
|
protected void run(final Result result) throws Throwable {
|
|
manager.editExternalAnnotation(method, AnnotationUtil.NULLABLE, annotationFromText.getParameterList().getAttributes());
|
|
}
|
|
}.execute();
|
|
stopListeningAndCheckEvents();
|
|
|
|
startListening(parameter, AnnotationUtil.NOT_NULL, true);
|
|
new WriteCommandAction(myProject) {
|
|
@Override
|
|
protected void run(final Result result) throws Throwable {
|
|
manager.editExternalAnnotation(parameter, AnnotationUtil.NOT_NULL, annotationFromText.getParameterList().getAttributes());
|
|
}
|
|
}.execute();
|
|
stopListeningAndCheckEvents();
|
|
|
|
assertMethodAndParameterAnnotationsValues(manager, method, parameter, "\"bar\"");
|
|
|
|
myFixture.checkResultByFile("content/annoMultiRoot/root1/multiRoot/annotations.xml",
|
|
"content/annoMultiRoot/root1/multiRoot/annotations_after.xml", false);
|
|
myFixture.checkResultByFile("content/annoMultiRoot/root2/multiRoot/annotations.xml",
|
|
"content/annoMultiRoot/root2/multiRoot/annotations_after.xml", false);
|
|
}
|
|
|
|
public void testListenerNotifiedWhenOperationsFail() {
|
|
addLibrary(); // no annotation roots: all operations should fail
|
|
myFixture.configureByFiles("lib/p/Test.java");
|
|
final PsiMethod method = ((PsiJavaFile)myFixture.getFile()).getClasses()[0].getMethods()[0];
|
|
|
|
startListening(method, AnnotationUtil.NOT_NULL, false);
|
|
new WriteCommandAction(myProject){
|
|
@Override
|
|
protected void run(final Result result) throws Throwable {
|
|
ExternalAnnotationsManager.getInstance(myProject).annotateExternally(method, AnnotationUtil.NOT_NULL, myFixture.getFile(), null);
|
|
}
|
|
}.execute();
|
|
stopListeningAndCheckEvents();
|
|
|
|
startListening(method, AnnotationUtil.NOT_NULL, false);
|
|
new WriteCommandAction(myProject){
|
|
@Override
|
|
protected void run(final Result result) throws Throwable {
|
|
ExternalAnnotationsManager.getInstance(myProject).editExternalAnnotation(method, AnnotationUtil.NOT_NULL, null);
|
|
}
|
|
}.execute();
|
|
stopListeningAndCheckEvents();
|
|
|
|
startListening(method, AnnotationUtil.NOT_NULL, false);
|
|
new WriteCommandAction(myProject){
|
|
@Override
|
|
protected void run(final Result result) throws Throwable {
|
|
ExternalAnnotationsManager.getInstance(myProject).deannotate(method, AnnotationUtil.NOT_NULL);
|
|
}
|
|
}.execute();
|
|
stopListeningAndCheckEvents();
|
|
}
|
|
|
|
public void testListenerNotifiedOnExternalChanges() {
|
|
addDefaultLibrary();
|
|
myFixture.configureByFiles("/content/anno/p/annotations.xml");
|
|
myFixture.configureByFiles("lib/p/Test.java");
|
|
|
|
ExternalAnnotationsManager.getInstance(myProject).findExternalAnnotation(getOwner(), AnnotationUtil.NOT_NULL); // force creating service
|
|
|
|
startListeningForExternalChanges();
|
|
new WriteCommandAction(myProject){
|
|
@Override
|
|
protected void run(final Result result) throws Throwable {
|
|
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(myFixture.getTempDirPath() + "/content/anno/p/annotations.xml");
|
|
assert file != null;
|
|
String newText = " " + StreamUtil.readText(file.getInputStream()) + " "; // adding newspace to the beginning and end of file
|
|
FileUtil.writeToFile(VfsUtil.virtualToIoFile(file), newText); // writing using java.io.File to make this change external
|
|
file.refresh(false, false);
|
|
}
|
|
}.execute();
|
|
stopListeningAndCheckEvents();
|
|
}
|
|
|
|
private class DefaultAnnotationsListener extends ExternalAnnotationsListener.Adapter {
|
|
@Override
|
|
public void afterExternalAnnotationChanging(@NotNull PsiModifierListOwner owner, @NotNull String annotationFQName,
|
|
boolean successful) {
|
|
System.err.println("Unexpected ExternalAnnotationsListener.afterExternalAnnotationChanging event produced");
|
|
System.err.println("owner = [" + owner + "], annotationFQName = [" + annotationFQName + "], successful = [" + successful + "]");
|
|
myUnexpectedEventWasProduced = true;
|
|
}
|
|
|
|
@Override
|
|
public void externalAnnotationsChangedExternally() {
|
|
System.err.println("Unexpected ExternalAnnotationsListener.externalAnnotationsChangedExternally event produced");
|
|
myUnexpectedEventWasProduced = true;
|
|
}
|
|
}
|
|
}
|