mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
Java: Provide completion inside literal argument of Class.forName() and ClassLoader.loadClass() (IDEA-167267)
This commit is contained in:
@@ -30,7 +30,9 @@ public class JavaReflectionCompletionConfidence extends CompletionConfidence {
|
||||
@Override
|
||||
public ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) {
|
||||
final PsiElement literal = contextElement.getParent();
|
||||
if (literal != null && JavaReflectionReferenceContributor.PATTERN.accepts(literal)) {
|
||||
if (literal != null &&
|
||||
(JavaReflectionReferenceContributor.PATTERN.accepts(literal) ||
|
||||
JavaReflectionReferenceContributor.CLASS_PATTERN.accepts(literal))) {
|
||||
return ThreeState.NO;
|
||||
}
|
||||
return super.shouldSkipAutopopup(contextElement, psiFile, offset);
|
||||
|
||||
@@ -16,13 +16,15 @@
|
||||
package com.intellij.psi.impl.source.resolve.reference.impl;
|
||||
|
||||
import com.intellij.patterns.PsiJavaElementPattern;
|
||||
import com.intellij.psi.PsiLiteral;
|
||||
import com.intellij.psi.PsiReferenceContributor;
|
||||
import com.intellij.psi.PsiReferenceRegistrar;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.patterns.PsiJavaPatterns.psiLiteral;
|
||||
import static com.intellij.patterns.PsiJavaPatterns.psiMethod;
|
||||
import static com.intellij.patterns.StandardPatterns.or;
|
||||
import static com.intellij.patterns.StandardPatterns.string;
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_LANG_CLASS;
|
||||
|
||||
@@ -37,8 +39,38 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor
|
||||
"getDeclaredMethod"))
|
||||
.definedInClass(JAVA_LANG_CLASS));
|
||||
|
||||
public static final PsiJavaElementPattern.Capture<PsiLiteral> CLASS_PATTERN =
|
||||
psiLiteral().methodCallParameter(or(
|
||||
psiMethod().withName(string().equalTo("forName")).definedInClass(JAVA_LANG_CLASS),
|
||||
psiMethod().withName(string().equalTo("loadClass")).definedInClass("java.lang.ClassLoader")));
|
||||
|
||||
@Override
|
||||
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
|
||||
registrar.registerReferenceProvider(PATTERN, new JavaReflectionReferenceProvider());
|
||||
registrar.registerReferenceProvider(PATTERN, new JavaReflectionReferenceProvider() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiReference[] getReferencesByMethod(@NotNull PsiLiteralExpression literalArgument,
|
||||
@NotNull PsiReferenceExpression methodReference,
|
||||
@NotNull ProcessingContext context) {
|
||||
|
||||
PsiExpression qualifier = methodReference.getQualifierExpression();
|
||||
return qualifier != null ? new PsiReference[]{new JavaLangClassMemberReference(literalArgument, qualifier)} : null;
|
||||
}
|
||||
});
|
||||
|
||||
registrar.registerReferenceProvider(CLASS_PATTERN, new JavaReflectionReferenceProvider() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiReference[] getReferencesByMethod(@NotNull PsiLiteralExpression literalArgument,
|
||||
@NotNull PsiReferenceExpression methodReference,
|
||||
@NotNull ProcessingContext context) {
|
||||
|
||||
String referenceName = methodReference.getReferenceName();
|
||||
if ("forName".equals(referenceName) || "loadClass".equals(referenceName)) {
|
||||
return new JavaClassReferenceProvider().getReferencesByElement(literalArgument, context);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ package com.intellij.psi.impl.source.resolve.reference.impl;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
*/
|
||||
public class JavaReflectionReferenceProvider extends PsiReferenceProvider {
|
||||
abstract class JavaReflectionReferenceProvider extends PsiReferenceProvider {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
|
||||
@@ -34,9 +35,9 @@ public class JavaReflectionReferenceProvider extends PsiReferenceProvider {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
if (grandParent instanceof PsiMethodCallExpression) {
|
||||
PsiReferenceExpression methodReference = ((PsiMethodCallExpression)grandParent).getMethodExpression();
|
||||
PsiExpression qualifier = methodReference.getQualifierExpression();
|
||||
if (qualifier != null) {
|
||||
return new PsiReference[]{new JavaLangClassMemberReference(literal, qualifier)};
|
||||
PsiReference[] references = getReferencesByMethod(literal, methodReference, context);
|
||||
if (references != null) {
|
||||
return references;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,4 +45,9 @@ public class JavaReflectionReferenceProvider extends PsiReferenceProvider {
|
||||
}
|
||||
return PsiReference.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract PsiReference[] getReferencesByMethod(@NotNull PsiLiteralExpression literalArgument,
|
||||
@NotNull PsiReferenceExpression methodReference,
|
||||
@NotNull ProcessingContext context);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class.forName("foo.bar.P<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class.forName("foo.bar.PublicClass");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class.forName("foo.bar.PublicClass.<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class.forName("foo.bar.PublicClass.NestedClass");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import foo.bar.one.*;
|
||||
import foo.bar.two.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class.forName("foo.bar.<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import foo.bar.one.*;
|
||||
import foo.bar.two.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class.forName("foo.bar.one");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Thread.currentThread().getContextClassLoader().loadClass("foo.bar.<caret>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Thread.currentThread().getContextClassLoader().loadClass("foo.bar.PublicClass");
|
||||
}
|
||||
}
|
||||
@@ -142,6 +142,29 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
|
||||
doTest(1, "num", "num2", "num3");
|
||||
}
|
||||
|
||||
|
||||
public void testClassForNameClasses() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass {}");
|
||||
myFixture.addClass("package foo.bar; class PackageLocalClass {}");
|
||||
doTest(1, "PackageLocalClass", "PublicClass");
|
||||
}
|
||||
|
||||
public void testClassForNamePackages() {
|
||||
myFixture.addClass("package foo.bar.one; public class FirstClass {}");
|
||||
myFixture.addClass("package foo.bar.two; public class SecondClass {}");
|
||||
doTest(0, "one", "two");
|
||||
}
|
||||
|
||||
public void testClassForNameNested() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass { public static class NestedClass {} }");
|
||||
doTest(0, "NestedClass");
|
||||
}
|
||||
|
||||
public void testWithClassLoader() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass {}");
|
||||
doTest(0, "PublicClass");
|
||||
}
|
||||
|
||||
private void doTest(int index, String... expected) {
|
||||
configureByFile(getTestName(false) + ".java");
|
||||
assertStringItems(expected);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.navigation
|
||||
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
class JavaReflectionClassNavigationTest : LightCodeInsightFixtureTestCase() {
|
||||
|
||||
fun testPublicClass() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass {}")
|
||||
doTest("foo.bar.PublicClass")
|
||||
}
|
||||
|
||||
fun testPublicInnerClass() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass { public class PublicInnerClass {} }")
|
||||
doTest("foo.bar.PublicClass.PublicInnerClass")
|
||||
}
|
||||
|
||||
fun testPublicInnerClass2() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass { public class PublicInnerClass {} }")
|
||||
doTest("foo.bar.<caret>PublicClass.PublicInnerClass")
|
||||
}
|
||||
|
||||
fun testPackageLocalClass() {
|
||||
myFixture.addClass("package foo.bar; class PackageLocalClass {}")
|
||||
doTest("foo.bar.PackageLocalClass")
|
||||
}
|
||||
|
||||
fun testPrivateInnerClass() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass { private class PrivateInnerClass {} }")
|
||||
doTest("foo.bar.PublicClass.PrivateInnerClass")
|
||||
}
|
||||
|
||||
fun testPrivateInnerClass2() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass { private class PrivateInnerClass {} }")
|
||||
doTest("foo.bar.<caret>PublicClass.PrivateInnerClass")
|
||||
}
|
||||
|
||||
fun testWithClassLoader() {
|
||||
myFixture.addClass("package foo.bar; public class PublicClass {}")
|
||||
doTest("foo.bar.<caret>PublicClass.PrivateInnerClass", { "Thread.currentThread().getContextClassLoader().loadClass(\"$it\")" })
|
||||
}
|
||||
|
||||
private fun doTest(className: String, usageFormatter: (String) -> String = { "Class.forName(\"$it\")" }) {
|
||||
val caretPos = className.indexOf("<caret>")
|
||||
val atCaret: String
|
||||
var expectedName = className
|
||||
if (caretPos >= 0) {
|
||||
atCaret = className
|
||||
val dotPos = className.indexOf(".", caretPos + 1)
|
||||
if (dotPos >= 0) expectedName = className.substring(0, dotPos).replace("<caret>", "")
|
||||
}
|
||||
else {
|
||||
atCaret = className + "<caret>"
|
||||
}
|
||||
myFixture.configureByText("Main.java",
|
||||
"""import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
${usageFormatter(atCaret)};
|
||||
}
|
||||
}""")
|
||||
val reference = myFixture.file.findReferenceAt(myFixture.caretOffset)
|
||||
assertNotNull("No reference at the caret", reference)
|
||||
val resolved = reference!!.resolve()
|
||||
assertTrue("Class not resolved: " + reference.canonicalText, resolved is PsiClass)
|
||||
val psiClass = resolved as? PsiClass
|
||||
assertEquals("Class name", expectedName, psiClass?.qualifiedName)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user