add dependency on external library quick-fix: API simplified, duplicated code removed

This commit is contained in:
nik
2015-07-29 08:31:13 +03:00
parent 378bceb399
commit 3338d30023
18 changed files with 493 additions and 431 deletions
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2015 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.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryDescriptor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author nik
*/
class AddExternalLibraryToDependenciesQuickFix extends OrderEntryFix {
private final Module myCurrentModule;
private final PsiReference myReference;
private final ExternalLibraryDescriptor myLibraryDescriptor;
private final String myQualifiedClassName;
public AddExternalLibraryToDependenciesQuickFix(@NotNull Module currentModule,
@NotNull ExternalLibraryDescriptor libraryDescriptor, @NotNull PsiReference reference,
@Nullable String qualifiedClassName) {
myCurrentModule = currentModule;
myReference = reference;
myLibraryDescriptor = libraryDescriptor;
myQualifiedClassName = qualifiedClassName;
}
@Nls
@NotNull
@Override
public String getText() {
return "Add '" + myLibraryDescriptor.getPresentableName() + "' to classpath";
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return getText();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return !project.isDisposed() && !myCurrentModule.isDisposed();
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
List<String> classesRoots = myLibraryDescriptor.locateLibraryClassesRoots(myCurrentModule);
if (!classesRoots.isEmpty()) {
String libraryName = classesRoots.size() > 1 ? myLibraryDescriptor.getPresentableName() : null;
addJarsToRootsAndImportClass(classesRoots, libraryName, myCurrentModule, editor, myReference,
myQualifiedClassName);
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2000-2015 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.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryDescriptor;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryResolver;
import com.intellij.openapi.module.Module;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author nik
*/
public class JetBrainsAnnotationsExternalLibraryResolver extends ExternalLibraryResolver {
private static final ExternalLibraryDescriptor ANNOTATIONS = new ExternalLibraryDescriptor("com.intellij", "annotations", null) {
@NotNull
@Override
public List<String> locateLibraryClassesRoots(@NotNull Module contextModule) {
return ContainerUtil.createMaybeSingletonList(OrderEntryFix.locateAnnotationsJar(contextModule));
}
};
@Nullable
@Override
public ExternalClassResolveResult resolveClass(@NotNull String shortClassName, @NotNull ThreeState isAnnotation) {
if (AnnotationUtil.isJetbrainsAnnotation(shortClassName)) {
return new ExternalClassResolveResult("org.jetbrains.annotations." + shortClassName, ANNOTATIONS);
}
return null;
}
}
@@ -19,11 +19,13 @@ import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.actions.AddImportAction;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryDescriptor;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryResolver;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryResolver.ExternalClassResolveResult;
import com.intellij.codeInsight.daemon.quickFix.MissingDependencyFixProvider;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
@@ -48,6 +50,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
@@ -91,7 +94,7 @@ public abstract class OrderEntryFix implements IntentionAction, LocalQuickFix {
@Nullable
public static List<LocalQuickFix> registerFixes(@NotNull final QuickFixActionRegistrar registrar, @NotNull final PsiReference reference) {
final PsiElement psiElement = reference.getElement();
@NonNls final String referenceName = reference.getRangeInElement().substring(psiElement.getText());
@NonNls final String shortReferenceName = reference.getRangeInElement().substring(psiElement.getText());
Project project = psiElement.getProject();
PsiFile containingFile = psiElement.getContainingFile();
@@ -114,65 +117,33 @@ public abstract class OrderEntryFix implements IntentionAction, LocalQuickFix {
return providedFixes;
}
if (isAnnotation(psiElement) && AnnotationUtil.isJetbrainsAnnotation(referenceName)) {
@NonNls final String className = "org.jetbrains.annotations." + referenceName;
PsiClass found =
JavaPsiFacade.getInstance(project).findClass(className, currentModule.getModuleWithDependenciesAndLibrariesScope(true));
if (found != null) return null; //no need to add junit to classpath
final OrderEntryFix platformFix = new OrderEntryFix() {
@Override
@NotNull
public String getText() {
return QuickFixBundle.message("orderEntry.fix.add.annotations.jar.to.classpath");
List<LocalQuickFix> result = new ArrayList<LocalQuickFix>();
JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
String fullReferenceText = reference.getCanonicalText();
for (ExternalLibraryResolver resolver : ExternalLibraryResolver.EP_NAME.getExtensions()) {
final ExternalClassResolveResult resolveResult = resolver.resolveClass(shortReferenceName, isReferenceToAnnotation(psiElement));
OrderEntryFix fix = null;
if (resolveResult != null && psiFacade.findClass(resolveResult.getQualifiedClassName(), currentModule.getModuleWithDependenciesAndLibrariesScope(true)) == null) {
fix = new AddExternalLibraryToDependenciesQuickFix(currentModule, resolveResult.getLibrary(), reference, resolveResult.getQualifiedClassName());
}
else if (!fullReferenceText.equals(shortReferenceName)) {
ExternalLibraryDescriptor descriptor = resolver.resolvePackage(fullReferenceText);
if (descriptor != null) {
fix = new AddExternalLibraryToDependenciesQuickFix(currentModule, descriptor, reference, null);
}
@Override
@NotNull
public String getFamilyName() {
return getText();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return !project.isDisposed() && !currentModule.isDisposed();
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
final String libraryPath = locateAnnotationsJar(currentModule);
if (libraryPath != null) {
new WriteCommandAction(project) {
@Override
protected void run(@NotNull final Result result) throws Throwable {
addJarsToRootsAndImportClass(Collections.singletonList(libraryPath), null, currentModule, editor, reference,
"org.jetbrains.annotations." + referenceName);
}
}.execute();
}
}
});
}
};
final OrderEntryFix providedFix = provideFix(new Function<MissingDependencyFixProvider, OrderEntryFix>() {
@Override
public OrderEntryFix fun(MissingDependencyFixProvider provider) {
return provider.getJetbrainsAnnotationFix(reference, platformFix, currentModule);
}
});
final OrderEntryFix fix = ObjectUtils.notNull(providedFix, platformFix);
registrar.register(fix);
return Collections.singletonList((LocalQuickFix)fix);
}
if (fix != null) {
registrar.register(fix);
result.add(fix);
}
}
if (!result.isEmpty()) {
return result;
}
List<LocalQuickFix> result = new ArrayList<LocalQuickFix>();
Set<Object> librariesToAdd = new THashSet<Object>();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(psiElement.getProject());
PsiClass[] classes = PsiShortNamesCache.getInstance(project).getClassesByName(referenceName, GlobalSearchScope.allScope(project));
PsiClass[] classes = PsiShortNamesCache.getInstance(project).getClassesByName(shortReferenceName, GlobalSearchScope.allScope(project));
List<PsiClass> allowedDependencies = filterAllowedDependencies(psiElement, classes);
if (allowedDependencies.isEmpty()) {
return result;
@@ -283,8 +254,17 @@ public abstract class OrderEntryFix implements IntentionAction, LocalQuickFix {
return result;
}
private static boolean isAnnotation(final PsiElement psiElement) {
return PsiTreeUtil.getParentOfType(psiElement, PsiAnnotation.class) != null && PsiUtil.isLanguageLevel5OrHigher(psiElement);
private static ThreeState isReferenceToAnnotation(final PsiElement psiElement) {
if (!PsiUtil.isLanguageLevel5OrHigher(psiElement)) {
return ThreeState.NO;
}
if (PsiTreeUtil.getParentOfType(psiElement, PsiAnnotation.class) != null) {
return ThreeState.YES;
}
if (PsiTreeUtil.getParentOfType(psiElement, PsiImportStatement.class) != null) {
return ThreeState.UNSURE;
}
return ThreeState.NO;
}
/**
@@ -301,7 +281,7 @@ public abstract class OrderEntryFix implements IntentionAction, LocalQuickFix {
final String libraryName,
@NotNull final Module currentModule, @Nullable final Editor editor,
@Nullable final PsiReference reference,
@NonNls final String className) {
@Nullable @NonNls final String className) {
addJarsToRoots(jarPaths, libraryName, currentModule, reference != null ? reference.getElement() : null);
final Project project = currentModule.getProject();
@@ -0,0 +1,59 @@
/*
* Copyright 2000-2015 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.codeInsight.daemon.quickFix;
import com.intellij.openapi.module.Module;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author nik
*/
public abstract class ExternalLibraryDescriptor {
private final String myLibraryGroupId;
private final String myLibraryArtifactId;
private final String myMinVersion;
public ExternalLibraryDescriptor(@NotNull String libraryGroupId, @NotNull String libraryArtifactId, @Nullable String minVersion) {
myLibraryGroupId = libraryGroupId;
myLibraryArtifactId = libraryArtifactId;
myMinVersion = minVersion;
}
@NotNull
public String getLibraryGroupId() {
return myLibraryGroupId;
}
@NotNull
public String getLibraryArtifactId() {
return myLibraryArtifactId;
}
@Nullable
public String getMinVersion() {
return myMinVersion;
}
public String getPresentableName() {
return myLibraryArtifactId;
}
@NotNull
public abstract List<String> locateLibraryClassesRoots(@NotNull Module contextModule);
}
@@ -0,0 +1,55 @@
/*
* Copyright 2000-2015 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.codeInsight.daemon.quickFix;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.util.ThreeState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author nik
*/
public abstract class ExternalLibraryResolver {
public static final ExtensionPointName<ExternalLibraryResolver> EP_NAME = ExtensionPointName.create("com.intellij.codeInsight.externalLibraryResolver");
@Nullable
public abstract ExternalClassResolveResult resolveClass(@NotNull String shortClassName, @NotNull ThreeState isAnnotation);
@Nullable
public ExternalLibraryDescriptor resolvePackage(@NotNull String packageName) {
return null;
}
public static class ExternalClassResolveResult {
private final String myQualifiedClassName;
private final ExternalLibraryDescriptor myLibrary;
public ExternalClassResolveResult(String qualifiedClassName,
ExternalLibraryDescriptor library) {
myQualifiedClassName = qualifiedClassName;
myLibrary = library;
}
public String getQualifiedClassName() {
return myQualifiedClassName;
}
public ExternalLibraryDescriptor getLibrary() {
return myLibrary;
}
}
}
@@ -19,15 +19,12 @@ import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.testIntegration.JavaTestFramework;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -51,22 +48,6 @@ public abstract class MissingDependencyFixProvider {
return null;
}
@Nullable
public OrderEntryFix getJUnitFix(@NotNull PsiReference reference,
@NotNull OrderEntryFix platformFix,
@NotNull Module currentModule,
@NotNull JavaTestFramework framework,
@NotNull String className) {
return null;
}
@Nullable
public OrderEntryFix getJetbrainsAnnotationFix(@NotNull PsiReference reference,
@NotNull OrderEntryFix platformFix,
@NotNull Module currentModule) {
return null;
}
@Nullable
public OrderEntryFix getAddModuleDependencyFix(@NotNull PsiReference reference,
@NotNull OrderEntryFix platformFix,
@@ -0,0 +1,65 @@
/*
* Copyright 2000-2015 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.codeInspection.concurrencyAnnotations;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryDescriptor;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryResolver;
import com.intellij.openapi.module.Module;
import com.intellij.util.PathUtil;
import com.intellij.util.ThreeState;
import net.jcip.annotations.GuardedBy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
/**
* @author nik
*/
public class JCiPExternalLibraryResolver extends ExternalLibraryResolver {
private static final ExternalLibraryDescriptor JDCIP_LIBRARY_DESCRIPTOR =
new ExternalLibraryDescriptor("net.jcip", "jcip-annotations", null) {
@NotNull
@Override
public List<String> locateLibraryClassesRoots(@NotNull Module contextModule) {
return Collections.singletonList(PathUtil.getJarPathForClass(GuardedBy.class));
}
@Override
public String getPresentableName() {
return "jcip-annotations.jar";
}
};
@Nullable
@Override
public ExternalClassResolveResult resolveClass(@NotNull String shortClassName, @NotNull ThreeState isAnnotation) {
if (JCiPUtil.isJCiPAnnotation(shortClassName) && isAnnotation == ThreeState.YES) {
return new ExternalClassResolveResult("net.jcip.annotations." + shortClassName, JDCIP_LIBRARY_DESCRIPTOR);
}
return null;
}
@Nullable
@Override
public ExternalLibraryDescriptor resolvePackage(@NotNull String packageName) {
if (packageName.equals("net.jcip.annotations")) {
return JDCIP_LIBRARY_DESCRIPTOR;
}
return null;
}
}
@@ -1,93 +0,0 @@
/*
* Copyright 2000-2009 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: 30-Jul-2007
*/
package com.intellij.codeInspection.concurrencyAnnotations;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PathUtil;
import net.jcip.annotations.GuardedBy;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
public class JCiPOrderEntryFix implements IntentionAction {
private static final Logger LOG = Logger.getInstance("#" + JCiPOrderEntryFix.class.getName());
@Override
@NotNull
public String getText() {
return "Add jcip-annotations.jar to classpath";
}
@Override
@NotNull
public String getFamilyName() {
return getText();
}
@Override
public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) {
if (!(file instanceof PsiJavaFile)) return false;
final PsiReference reference = TargetElementUtil.findReference(editor);
if (!(reference instanceof PsiJavaCodeReferenceElement)) return false;
if (reference.resolve() != null) return false;
@NonNls final String referenceName = ((PsiJavaCodeReferenceElement)reference).getReferenceName();
if (referenceName == null) return false;
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) return false;
if (fileIndex.getModuleForFile(virtualFile) == null) return false;
if (!(((PsiJavaCodeReferenceElement)reference).getParent() instanceof PsiAnnotation &&
PsiUtil.isLanguageLevel5OrHigher(((PsiJavaCodeReferenceElement)reference)))) return false;
if (!JCiPUtil.isJCiPAnnotation(referenceName)) return false;
return true;
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
final PsiJavaCodeReferenceElement reference = (PsiJavaCodeReferenceElement)TargetElementUtil.findReference(editor);
LOG.assertTrue(reference != null);
String jarPath = PathUtil.getJarPathForClass(GuardedBy.class);
final VirtualFile virtualFile = file.getVirtualFile();
LOG.assertTrue(virtualFile != null);
OrderEntryFix.addJarsToRootsAndImportClass(Collections.singletonList(jarPath), null, ModuleUtil.findModuleForFile(virtualFile, project), editor,
reference, "net.jcip.annotations." + reference.getReferenceName());
}
@Override
public boolean startInWriteAction() {
return true;
}
}
@@ -1,4 +1,4 @@
// "Add JUnit to classpath" "true"
// "Add 'JUnit3' to classpath" "true"
package x;
public class DoTest extends TestCase<caret>{
@@ -1,4 +1,4 @@
// "Add JUnit to classpath" "true"
// "Add 'JUnit4' to classpath" "true"
package x;
public class DoTest4 {
@@ -1,4 +1,4 @@
// "Add JUnit to classpath" "true"
// "Add 'JUnit4' to classpath" "true"
package x;
public class DoTest4junit {
+1 -1
View File
@@ -60,7 +60,7 @@
<stacktrace.fold substring="at junit.framework.TestResult"/>
<programRunner implementation="com.intellij.execution.junit.JUnitDebuggerRunner"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="com.intellij.execution.junit.codeInsight.JUnitUnresolvedReferenceQuickFixProvider"/>
<codeInsight.externalLibraryResolver implementation="com.intellij.execution.junit.codeInsight.JUnitExternalLibraryResolver"/>
<junitListener implementation="com.intellij.junit4.JUnitTestDiscoveryListener"/>
<configurationType implementation="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationType"/>
<runConfigurationProducer implementation="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer"/>
@@ -0,0 +1,82 @@
/*
* Copyright 2000-2015 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.execution.junit.codeInsight;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryDescriptor;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryResolver;
import com.intellij.execution.junit.JUnit3Framework;
import com.intellij.execution.junit.JUnit4Framework;
import com.intellij.openapi.module.Module;
import com.intellij.testIntegration.JavaTestFramework;
import com.intellij.testIntegration.TestFramework;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
/**
* @author nik
*/
public class JUnitExternalLibraryResolver extends ExternalLibraryResolver {
public static final ExternalLibraryDescriptor JUNIT3 = createJUnitDescriptor("3", JUnit3Framework.class);
public static final ExternalLibraryDescriptor JUNIT4 = createJUnitDescriptor("4", JUnit4Framework.class);
@NotNull
private static ExternalLibraryDescriptor createJUnitDescriptor(final String version, final Class<? extends JavaTestFramework> frameworkClass) {
return new ExternalLibraryDescriptor("junit", "junit", version) {
@NotNull
@Override
public List<String> locateLibraryClassesRoots(@NotNull Module contextModule) {
return TestFramework.EXTENSION_NAME.findExtension(frameworkClass).getLibraryPaths();
}
@Override
public String getPresentableName() {
return "JUnit" + version;
}
};
}
private static Set<String> JUNIT4_ANNOTATIONS = ContainerUtil.set(
"Test", "Ignore", "RunWith", "Before", "BeforeClass", "After", "AfterClass"
);
@Nullable
@Override
public ExternalClassResolveResult resolveClass(@NotNull String shortClassName, @NotNull ThreeState isAnnotation) {
if ("TestCase".equals(shortClassName)) {
return new ExternalClassResolveResult("junit.framework.TestCase", JUNIT3);
}
if (isAnnotation == ThreeState.YES && JUNIT4_ANNOTATIONS.contains(shortClassName)) {
return new ExternalClassResolveResult("org.junit." + shortClassName, JUNIT4);
}
return null;
}
@Nullable
@Override
public ExternalLibraryDescriptor resolvePackage(@NotNull String packageName) {
if (packageName.equals("org.junit")) {
return JUNIT4;
}
if (packageName.equals("junit.framework")) {
return JUNIT3;
}
return null;
}
}
@@ -1,143 +0,0 @@
/*
* Copyright 2000-2015 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.execution.junit.codeInsight;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.quickfix.MissingDependencyFixUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInsight.daemon.quickFix.MissingDependencyFixProvider;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.execution.junit.JUnit3Framework;
import com.intellij.execution.junit.JUnit4Framework;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testIntegration.JavaTestFramework;
import com.intellij.testIntegration.TestFramework;
import com.intellij.util.Function;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author nik
*/
public class JUnitUnresolvedReferenceQuickFixProvider extends UnresolvedReferenceQuickFixProvider<PsiJavaCodeReferenceElement> {
@Override
public void registerFixes(@NotNull final PsiJavaCodeReferenceElement reference, @NotNull QuickFixActionRegistrar registrar) {
final PsiElement psiElement = reference.getElement();
@NonNls final String referenceName = reference.getRangeInElement().substring(psiElement.getText());
Project project = psiElement.getProject();
PsiFile containingFile = psiElement.getContainingFile();
if (containingFile == null) return;
final VirtualFile classVFile = containingFile.getVirtualFile();
if (classVFile == null) return;
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
final Module currentModule = fileIndex.getModuleForFile(classVFile);
if (currentModule == null) return;
final JavaTestFramework framework;
@NonNls final String className;
if ("TestCase".equals(referenceName)) {
framework = TestFramework.EXTENSION_NAME.findExtension(JUnit3Framework.class);
className = "junit.framework.TestCase";
}
else if (PsiTreeUtil.getParentOfType(psiElement, PsiAnnotation.class) != null && isJunitAnnotationName(referenceName, psiElement)) {
framework = TestFramework.EXTENSION_NAME.findExtension(JUnit4Framework.class);
className = "org.junit." + referenceName;
}
else {
return;
}
PsiClass found = JavaPsiFacade.getInstance(project).findClass(className, currentModule.getModuleWithDependenciesAndLibrariesScope(true));
if (found != null) return;
final OrderEntryFix platformFix = new OrderEntryFix() {
@Override
@NotNull
public String getText() {
return QuickFixBundle.message("orderEntry.fix.add.junit.jar.to.classpath");
}
@Override
@NotNull
public String getFamilyName() {
return getText();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return !project.isDisposed() && !currentModule.isDisposed();
}
@Override
public void invoke(@NotNull Project project, @Nullable Editor editor, PsiFile file) {
List<String> jarPaths = framework.getLibraryPaths();
String libraryName = jarPaths.size() == 1 ? null : framework.getName();
addJarsToRootsAndImportClass(jarPaths, libraryName, currentModule, editor, reference, className);
}
};
final OrderEntryFix providedFix = MissingDependencyFixUtil.provideFix(new Function<MissingDependencyFixProvider, OrderEntryFix>() {
@Override
public OrderEntryFix fun(MissingDependencyFixProvider provider) {
return provider.getJUnitFix(reference, platformFix, currentModule, framework, className);
}
});
final OrderEntryFix fix = ObjectUtils.notNull(providedFix, platformFix);
registrar.register(fix);
}
private static boolean isJunitAnnotationName(@NonNls final String referenceName, @NotNull final PsiElement psiElement) {
if ("Test".equals(referenceName) || "Ignore".equals(referenceName) || "RunWith".equals(referenceName) ||
"Before".equals(referenceName) || "BeforeClass".equals(referenceName) ||
"After".equals(referenceName) || "AfterClass".equals(referenceName)) {
return true;
}
final PsiElement parent = psiElement.getParent();
if (parent != null && !(parent instanceof PsiAnnotation)) {
final PsiReference reference = parent.getReference();
if (reference != null) {
final String referenceText = parent.getText();
if (isJunitAnnotationName(reference.getRangeInElement().substring(referenceText), parent)) {
final int lastDot = referenceText.lastIndexOf('.');
return lastDot > -1 && referenceText.substring(0, lastDot).equals("org.junit");
}
}
}
return false;
}
@NotNull
@Override
public Class<PsiJavaCodeReferenceElement> getReferenceClass() {
return PsiJavaCodeReferenceElement.class;
}
}
+2 -4
View File
@@ -57,10 +57,8 @@
groupName="TestNG" enabledByDefault="true" level="ERROR"
implementationClass="com.theoryinpractice.testng.inspection.DataProviderReturnTypeInspection"/>
<intentionAction>
<className>com.theoryinpractice.testng.intention.TestNGOrderEntryFix</className>
</intentionAction>
<testFramework implementation="com.theoryinpractice.testng.TestNGFramework"/>
<testFramework implementation="com.theoryinpractice.testng.TestNGFramework"/>
<codeInsight.externalLibraryResolver implementation="com.theoryinpractice.testng.intention.TestNGExternalLibraryResolver"/>
<standardResource url="http://testng.org/testng-1.0.dtd" path="resources/standardSchemas/testng-1.0.dtd"/>
<overrideImplementsAnnotationsHandler implementation="com.theoryinpractice.testng.intention.OverrideImplementsTestNGAnnotationsHandler"/>
<forcedElementWeigher implementation="com.theoryinpractice.testng.TestNGForcedElementWeigher"/>
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2015 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.theoryinpractice.testng.intention;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryDescriptor;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryResolver;
import com.intellij.openapi.module.Module;
import com.intellij.util.PathUtil;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author nik
*/
public class TestNGExternalLibraryResolver extends ExternalLibraryResolver {
private static final Set<String> TEST_NG_ANNOTATIONS = ContainerUtil.set(
"Test", "BeforeClass", "BeforeGroups", "BeforeMethod", "BeforeSuite", "BeforeTest", "AfterClass", "AfterGroups", "AfterMethod",
"AfterSuite", "AfterTest", "Configuration"
);
private static final ExternalLibraryDescriptor TESTNG_DESCRIPTOR = new ExternalLibraryDescriptor("org.testng", "testng", null) {
@NotNull
@Override
public List<String> locateLibraryClassesRoots(@NotNull Module contextModule) {
return Collections.singletonList(PathUtil.getJarPathForClass(Test.class));
}
};
@Nullable
@Override
public ExternalClassResolveResult resolveClass(@NotNull String shortClassName, @NotNull ThreeState isAnnotation) {
if (TEST_NG_ANNOTATIONS.contains(shortClassName)) {
return new ExternalClassResolveResult("org.testng.annotations." + shortClassName, TESTNG_DESCRIPTOR);
}
return null;
}
}
@@ -1,105 +0,0 @@
/*
* Copyright 2000-2009 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: 30-Jul-2007
*/
package com.theoryinpractice.testng.intention;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.testng.annotations.Test;
import java.util.Collections;
public class TestNGOrderEntryFix implements IntentionAction {
private static final Logger LOG = Logger.getInstance("#" + TestNGOrderEntryFix.class.getName());
@NotNull
public String getText() {
return "Add testng.jar to classpath";
}
@NotNull
public String getFamilyName() {
return getText();
}
public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) {
if (!(file instanceof PsiJavaFile)) return false;
if (!file.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return false;
final PsiReference reference = TargetElementUtil.findReference(editor);
if (!(reference instanceof PsiJavaCodeReferenceElement)) return false;
if (reference.resolve() != null) return false;
@NonNls final String referenceName = ((PsiJavaCodeReferenceElement)reference).getReferenceName();
if (referenceName == null) return false;
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) return false;
if (fileIndex.getModuleForFile(virtualFile) == null) return false;
if (!(((PsiJavaCodeReferenceElement)reference).getParent() instanceof PsiAnnotation &&
PsiUtil.isLanguageLevel5OrHigher(((PsiJavaCodeReferenceElement)reference)))) return false;
if (!isTestNGAnnotationName(referenceName)) return false;
return true;
}
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
final PsiJavaCodeReferenceElement reference = (PsiJavaCodeReferenceElement)TargetElementUtil.findReference(editor);
LOG.assertTrue(reference != null);
String jarPath = PathUtil.getJarPathForClass(Test.class);
final VirtualFile virtualFile = file.getVirtualFile();
LOG.assertTrue(virtualFile != null);
OrderEntryFix.addJarsToRootsAndImportClass(Collections.singletonList(jarPath), null, ModuleUtilCore.findModuleForFile(virtualFile, project),
editor, reference, "org.testng.annotations." + reference.getReferenceName());
}
public boolean startInWriteAction() {
return true;
}
private static boolean isTestNGAnnotationName(@NonNls final String referenceName) {
return "Test".equals(referenceName) ||
"BeforeClass".equals(referenceName) ||
"BeforeGroups".equals(referenceName) ||
"BeforeMethod".equals(referenceName) ||
"BeforeSuite".equals(referenceName) ||
"BeforeTest".equals(referenceName) ||
"AfterClass".equals(referenceName) ||
"AfterGroups".equals(referenceName) ||
"AfterMethod".equals(referenceName) ||
"AfterSuite".equals(referenceName) ||
"AfterTest".equals(referenceName) ||
"Configuration".equals(referenceName);
}
}
+5 -4
View File
@@ -279,6 +279,8 @@
<!--suppress DeprecatedClassUsageInspection -->
<extensionPoint name="psi.clsCustomNavigationPolicy" interface="com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy"/>
<extensionPoint name="codeInsight.externalLibraryResolver" interface="com.intellij.codeInsight.daemon.quickFix.ExternalLibraryResolver"/>
<extensionPoint name="vetoSPICondition" interface="com.intellij.openapi.util.Condition"/>
<extensionPoint name="hierarchy.referenceProcessor" interface="com.intellij.ide.hierarchy.call.CallReferenceProcessor"/>
@@ -978,10 +980,6 @@
</intentionAction>
<intentionAction>
<className>com.intellij.codeInspection.concurrencyAnnotations.JCiPOrderEntryFix</className>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.RemoveLiteralUnderscoresAction</className>
<category>Java/Numbers</category>
@@ -1638,6 +1636,9 @@
<generation.toStringClassFilter implementation="org.jetbrains.generate.tostring.GenerateToStringInterfaceFilter" order="last"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="com.intellij.jarFinder.FindJarQuickFixProvider"/>
<codeInsight.externalLibraryResolver
implementation="com.intellij.codeInsight.daemon.impl.quickfix.JetBrainsAnnotationsExternalLibraryResolver"/>
<codeInsight.externalLibraryResolver implementation="com.intellij.codeInspection.concurrencyAnnotations.JCiPExternalLibraryResolver"/>
<refactoring.pullUpHelperFactory language="JAVA" implementationClass="com.intellij.refactoring.memberPullUp.JavaPullUpHelperFactory"/>
<hierarchy.referenceProcessor implementation="com.intellij.ide.hierarchy.call.JavaCallReferenceProcessor"/>