(on-the-fly) static import of constant

This commit is contained in:
Anna Kozlova
2016-01-13 16:30:04 +01:00
parent a599a786ff
commit f7bbd49f75
12 changed files with 628 additions and 284 deletions

View File

@@ -39,6 +39,7 @@ public class DefaultQuickFixProvider extends UnresolvedReferenceQuickFixProvider
public void registerFixes(@NotNull PsiJavaCodeReferenceElement ref, @NotNull QuickFixActionRegistrar registrar) {
QuickFixFactory factory = QuickFixFactory.getInstance();
registrar.register(new ImportClassFix(ref));
registrar.register(new StaticImportConstantFix(ref));
registrar.register(factory.createSetupJDKFix());
OrderEntryFix.registerFixes(registrar, ref);

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2000-2016 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.completion.JavaCompletionUtil;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.proximity.PsiProximityComparator;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Processor;
import com.intellij.util.containers.LinkedMultiMap;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import java.util.*;
abstract class MyStaticMembersProcessor<T extends PsiMember> implements Processor<T> {
private final MultiMap<PsiClass, T> myDeprecated = new LinkedMultiMap<PsiClass, T>();
private final MultiMap<PsiClass, T> mySuggestions = new LinkedMultiMap<PsiClass, T>();
private final Map<PsiClass, Boolean> myPossibleClasses = new HashMap<PsiClass, Boolean>();
private final PsiElement myPlace;
private PsiType myExpectedType;
protected MyStaticMembersProcessor(PsiElement place) {
myPlace = place;
myExpectedType = PsiType.NULL;
}
protected abstract boolean isApplicable(T member, PsiElement place);
@NotNull
public List<T> getMembersToImport() {
final List<T> list = new ArrayList<T>();
final List<T> applicableList = new ArrayList<T>();
for (Map.Entry<PsiClass, Collection<T>> methodEntry : mySuggestions.entrySet()) {
registerMember(methodEntry.getKey(), methodEntry.getValue(), list, applicableList);
}
for (Map.Entry<PsiClass, Collection<T>> deprecatedMethod : myDeprecated.entrySet()) {
registerMember(deprecatedMethod.getKey(), deprecatedMethod.getValue(), list, applicableList);
}
List<T> result = applicableList.isEmpty() ? list : applicableList;
for (int i = result.size() - 1; i >= 0; i--) {
ProgressManager.checkCanceled();
T method = result.get(i);
// check for manually excluded
if (StaticImportMethodFix.isExcluded(method)) {
result.remove(i);
}
}
Collections.sort(result, new PsiProximityComparator(myPlace));
return result;
}
public PsiType getExpectedType() {
if (myExpectedType == PsiType.NULL) {
myExpectedType = getExpectedTypeInternal();
}
return myExpectedType;
}
private PsiType getExpectedTypeInternal() {
if (myPlace == null) return null;
final PsiElement parent = PsiUtil.skipParenthesizedExprUp(myPlace.getParent());
if (parent instanceof PsiVariable) {
if (myPlace.equals(PsiUtil.skipParenthesizedExprDown(((PsiVariable)parent).getInitializer()))) {
return ((PsiVariable)parent).getType();
}
}
else if (parent instanceof PsiAssignmentExpression) {
if (myPlace.equals(PsiUtil.skipParenthesizedExprDown(((PsiAssignmentExpression)parent).getRExpression()))) {
return ((PsiAssignmentExpression)parent).getLExpression().getType();
}
}
else if (parent instanceof PsiReturnStatement) {
final PsiElement psiElement = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, PsiMethod.class);
if (psiElement instanceof PsiLambdaExpression) {
return LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)psiElement).getFunctionalInterfaceType());
}
else if (psiElement instanceof PsiMethod) {
return ((PsiMethod)psiElement).getReturnType();
}
}
else if (parent instanceof PsiExpressionList) {
final PsiElement pParent = parent.getParent();
if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
final JavaResolveResult resolveResult = ((PsiCallExpression)pParent).resolveMethodGenerics();
final PsiElement psiElement = resolveResult.getElement();
if (psiElement instanceof PsiMethod) {
final PsiMethod psiMethod = (PsiMethod)psiElement;
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
final int idx = ArrayUtilRt.find(((PsiExpressionList)parent).getExpressions(), PsiUtil.skipParenthesizedExprUp(myPlace));
if (idx > -1 && parameters.length > 0) {
PsiType parameterType = parameters[Math.min(idx, parameters.length - 1)].getType();
if (idx >= parameters.length - 1) {
final PsiParameter lastParameter = parameters[parameters.length - 1];
if (lastParameter.isVarArgs()) {
parameterType = ((PsiEllipsisType)lastParameter.getType()).getComponentType();
}
}
return resolveResult.getSubstitutor().substitute(parameterType);
}
else {
return null;
}
}
}
}
else if (parent instanceof PsiLambdaExpression) {
return LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
}
return null;
}
@Override
public boolean process(T member) {
ProgressManager.checkCanceled();
if (JavaCompletionUtil.isInExcludedPackage(member, false) || !member.hasModifierProperty(PsiModifier.STATIC)) return true;
PsiFile file = member.getContainingFile();
final PsiClass containingClass = member.getContainingClass();
if (file instanceof PsiJavaFile
//do not show methods from default package
&& !((PsiJavaFile)file).getPackageName().isEmpty()) {
if (isEffectivelyDeprecated(member)) {
myDeprecated.putValue(containingClass, member);
return processCondition();
}
mySuggestions.putValue(containingClass, member);
}
return processCondition();
}
private boolean isEffectivelyDeprecated(T member) {
if (((PsiDocCommentOwner)member).isDeprecated()) {
return true;
}
PsiClass aClass = member.getContainingClass();
while (aClass != null) {
if (aClass.isDeprecated()) {
return true;
}
aClass = aClass.getContainingClass();
}
return false;
}
private boolean processCondition() {
return mySuggestions.size() + myDeprecated.size() < 50;
}
private void registerMember(PsiClass containingClass,
Collection<T> members,
List<T> list,
List<T> applicableList) {
final Boolean alreadyMentioned = myPossibleClasses.get(containingClass);
if (alreadyMentioned == Boolean.TRUE) return;
if (alreadyMentioned == null) {
if (!members.isEmpty()) {
list.add(members.iterator().next());
}
myPossibleClasses.put(containingClass, false);
}
for (T member : members) {
if (!PsiUtil.isAccessible(myPlace.getProject(), member, myPlace, containingClass)) {
continue;
}
if (isApplicable(member, myPlace)) {
applicableList.add(member);
myPossibleClasses.put(containingClass, true);
break;
}
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2000-2016 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.QuickFixBundle;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
public class StaticImportConstantFix extends StaticImportMemberFix<PsiField> {
private final SmartPsiElementPointer<PsiJavaCodeReferenceElement> myRef;
public StaticImportConstantFix(@NotNull PsiJavaCodeReferenceElement referenceElement) {
myRef = SmartPointerManager.getInstance(referenceElement.getProject()).createSmartPsiElementPointer(referenceElement);
}
@NotNull
@Override
protected String getBaseText() {
return "Static import constant";
}
@NotNull
@Override
protected String getMemberPresentableText(PsiField field) {
return PsiFormatUtil.formatVariable(field, PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
PsiFormatUtilBase.SHOW_FQ_NAME, PsiSubstitutor.EMPTY);
}
@NotNull
@Override
protected List<PsiField> getMembersToImport() {
final Project project = myRef.getProject();
PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project);
final PsiJavaCodeReferenceElement element = myRef.getElement();
String name = element != null ? element.getReferenceName() : null;
if (name == null) return Collections.emptyList();
final MyStaticMembersProcessor<PsiField> processor = new MyStaticMembersProcessor<PsiField>(element) {
@Override
protected boolean isApplicable(PsiField field, PsiElement place) {
final PsiType expectedType = getExpectedType();
return expectedType == null || TypeConversionUtil.isAssignable(expectedType, field.getType());
}
};
cache.processFieldsWithName(name, processor, element.getResolveScope(), null);
return processor.getMembersToImport();
}
@NotNull
protected StaticImportMethodQuestionAction<PsiField> createQuestionAction(List<PsiField> methodsToImport, @NotNull Project project, Editor editor) {
return new StaticImportMethodQuestionAction<PsiField>(project, editor, methodsToImport, myRef) {
@NotNull
@Override
protected String getPopupTitle() {
return QuickFixBundle.message("field.to.import.chooser.title");
}
};
}
@Nullable
@Override
protected PsiElement getElement() {
return myRef.getElement();
}
@Nullable
@Override
protected PsiElement getQualifierExpression() {
final PsiJavaCodeReferenceElement element = myRef.getElement();
return element != null ? element.getQualifier() : null;
}
@Nullable
@Override
protected PsiElement resolveRef() {
final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)getElement();
return referenceElement != null ? referenceElement.resolve() : null;
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2000-2016 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.FileModificationService;
import com.intellij.codeInsight.daemon.impl.ShowAutoImportPass;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.hint.QuestionAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.HintAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMember;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public abstract class StaticImportMemberFix<T extends PsiMember> implements IntentionAction, HintAction {
private List<T> candidates;
@NotNull protected abstract String getBaseText();
@NotNull protected abstract String getMemberPresentableText(T t);
@Override
@NotNull
public String getText() {
String text = getBaseText();
if (candidates != null && candidates.size() == 1) {
text += " '" + getMemberPresentableText(candidates.get(0)) + "'";
}
else {
text += "...";
}
return text;
}
@Override
@NotNull
public String getFamilyName() {
return getText();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return PsiUtil.isLanguageLevel5OrHigher(file)
&& file instanceof PsiJavaFile
&& getElement() != null
&& getElement().isValid()
&& getQualifierExpression() == null
&& resolveRef() == null
&& file.getManager().isInProject(file)
&& !(candidates == null ? candidates = getMembersToImport() : candidates).isEmpty()
;
}
@NotNull protected abstract List<T> getMembersToImport();
@NotNull protected abstract QuestionAction createQuestionAction(List<T> methodsToImport, @NotNull Project project, Editor editor);
@Nullable protected abstract PsiElement getElement();
@Nullable protected abstract PsiElement getQualifierExpression();
@Nullable protected abstract PsiElement resolveRef();
@Override
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) {
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final List<T> methodsToImport = getMembersToImport();
if (methodsToImport.isEmpty()) return;
createQuestionAction(methodsToImport, project, editor).execute();
}
});
}
private ImportClassFixBase.Result doFix(Editor editor) {
if (candidates.isEmpty()) {
return ImportClassFixBase.Result.POPUP_NOT_SHOWN;
}
final PsiElement element = getElement();
if (element == null) {
return ImportClassFixBase.Result.POPUP_NOT_SHOWN;
}
final QuestionAction action = createQuestionAction(candidates, element.getProject(), editor);
if (candidates.size() == 1 && ImportClassFixBase.canAddUnambiguousImport(element.getContainingFile())) {
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@Override
public void run() {
action.execute();
}
});
return ImportClassFixBase.Result.CLASS_AUTO_IMPORTED;
}
String hintText = ShowAutoImportPass.getMessage(candidates.size() > 1, getMemberPresentableText(candidates.get(0)));
if (!ApplicationManager.getApplication().isUnitTestMode() && !HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) {
final TextRange textRange = element.getTextRange();
HintManager.getInstance().showQuestionHint(editor, hintText,
textRange.getStartOffset(),
textRange.getEndOffset(), action);
}
return ImportClassFixBase.Result.POPUP_SHOWN;
}
@Override
public boolean startInWriteAction() {
return false;
}
@Override
public boolean showHint(@NotNull Editor editor) {
final PsiElement callExpression = getElement();
if (callExpression == null ||
getQualifierExpression() != null) {
return false;
}
ImportClassFixBase.Result result = doFix(editor);
return result == ImportClassFixBase.Result.POPUP_SHOWN || result == ImportClassFixBase.Result.CLASS_AUTO_IMPORTED;
}
}

View File

@@ -15,245 +15,56 @@
*/
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.JavaProjectCodeInsightSettings;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.ShowAutoImportPass;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.HintAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.*;
import com.intellij.psi.util.proximity.PsiProximityComparator;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Processor;
import com.intellij.util.containers.LinkedMultiMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Collections;
import java.util.List;
public class StaticImportMethodFix implements IntentionAction, HintAction {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.StaticImportMethodFix");
public class StaticImportMethodFix extends StaticImportMemberFix<PsiMethod> {
private final SmartPsiElementPointer<PsiMethodCallExpression> myMethodCall;
private List<PsiMethod> candidates;
public StaticImportMethodFix(@NotNull PsiMethodCallExpression methodCallExpression) {
myMethodCall = SmartPointerManager.getInstance(methodCallExpression.getProject()).createSmartPsiElementPointer(methodCallExpression);
}
@NotNull
@Override
@NotNull
public String getText() {
String text = QuickFixBundle.message("static.import.method.text");
if (candidates != null && candidates.size() == 1) {
text += " '" + getMethodPresentableText() + "'";
}
else {
text += "...";
}
return text;
protected String getBaseText() {
return QuickFixBundle.message("static.import.method.text");
}
@NotNull
private String getMethodPresentableText() {
return PsiFormatUtil.formatMethod(candidates.get(0), PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
PsiFormatUtilBase.SHOW_FQ_NAME, 0);
}
@Override
@NotNull
public String getFamilyName() {
return getText();
protected String getMemberPresentableText(PsiMethod method) {
return PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
PsiFormatUtilBase.SHOW_FQ_NAME, 0);
}
@NotNull
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return PsiUtil.isLanguageLevel5OrHigher(file)
&& file instanceof PsiJavaFile
&& myMethodCall.getElement() != null
&& myMethodCall.getElement().isValid()
&& myMethodCall.getElement().getMethodExpression().getQualifierExpression() == null
&& myMethodCall.getElement().resolveMethod() == null
&& file.getManager().isInProject(file)
&& !(candidates == null ? candidates = getMethodsToImport() : candidates).isEmpty()
;
}
private PsiType getExpectedType() {
final PsiMethodCallExpression methodCall = myMethodCall.getElement();
if (methodCall == null) return null;
final PsiElement parent = PsiUtil.skipParenthesizedExprUp(methodCall.getParent());
if (parent instanceof PsiVariable) {
if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(((PsiVariable)parent).getInitializer()))) {
return ((PsiVariable)parent).getType();
}
}
else if (parent instanceof PsiAssignmentExpression) {
if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(((PsiAssignmentExpression)parent).getRExpression()))) {
return ((PsiAssignmentExpression)parent).getLExpression().getType();
}
}
else if (parent instanceof PsiReturnStatement) {
final PsiElement psiElement = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, PsiMethod.class);
if (psiElement instanceof PsiLambdaExpression) {
return LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)psiElement).getFunctionalInterfaceType());
}
else if (psiElement instanceof PsiMethod) {
return ((PsiMethod)psiElement).getReturnType();
}
}
else if (parent instanceof PsiExpressionList) {
final PsiElement pParent = parent.getParent();
if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
final JavaResolveResult resolveResult = ((PsiCallExpression)pParent).resolveMethodGenerics();
final PsiElement psiElement = resolveResult.getElement();
if (psiElement instanceof PsiMethod) {
final PsiMethod psiMethod = (PsiMethod)psiElement;
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
final int idx = ArrayUtilRt.find(((PsiExpressionList)parent).getExpressions(), PsiUtil.skipParenthesizedExprUp(methodCall));
if (idx > -1 && parameters.length > 0) {
PsiType parameterType = parameters[Math.min(idx, parameters.length - 1)].getType();
if (idx >= parameters.length - 1) {
final PsiParameter lastParameter = parameters[parameters.length - 1];
if (lastParameter.isVarArgs()) {
parameterType = ((PsiEllipsisType)lastParameter.getType()).getComponentType();
}
}
return resolveResult.getSubstitutor().substitute(parameterType);
}
else {
return null;
}
}
}
}
else if (parent instanceof PsiLambdaExpression) {
return LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
}
return null;
}
@NotNull
private List<PsiMethod> getMethodsToImport() {
protected List<PsiMethod> getMembersToImport() {
final Project project = myMethodCall.getProject();
PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project);
final PsiMethodCallExpression element = myMethodCall.getElement();
PsiReferenceExpression reference = element.getMethodExpression();
final PsiExpressionList argumentList = element.getArgumentList();
String name = reference.getReferenceName();
final List<PsiMethod> list = new ArrayList<PsiMethod>();
if (name == null) return list;
GlobalSearchScope scope = element.getResolveScope();
final Map<PsiClass, Boolean> possibleClasses = new HashMap<PsiClass, Boolean>();
final PsiType expectedType = getExpectedType();
final List<PsiMethod> applicableList = new ArrayList<PsiMethod>();
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper();
final MultiMap<PsiClass, PsiMethod> deprecated = new LinkedMultiMap<PsiClass, PsiMethod>();
final MultiMap<PsiClass, PsiMethod> suggestions = new LinkedMultiMap<PsiClass, PsiMethod>();
class RegisterMethodsProcessor {
private void registerMethod(PsiClass containingClass, Collection<PsiMethod> methods) {
final Boolean alreadyMentioned = possibleClasses.get(containingClass);
if (alreadyMentioned == Boolean.TRUE) return;
if (alreadyMentioned == null) {
if (!methods.isEmpty()) {
list.add(methods.iterator().next());
}
possibleClasses.put(containingClass, false);
}
for (PsiMethod method : methods) {
if (!PsiUtil.isAccessible(project, method, element, containingClass)) {
continue;
}
PsiSubstitutor substitutorForMethod = resolveHelper
.inferTypeArguments(method.getTypeParameters(), method.getParameterList().getParameters(),
argumentList.getExpressions(),
PsiSubstitutor.EMPTY, element.getParent(), DefaultParameterTypeInferencePolicy.INSTANCE);
if (PsiUtil.isApplicable(method, substitutorForMethod, argumentList)) {
final PsiType returnType = substitutorForMethod.substitute(method.getReturnType());
if (expectedType == null || returnType == null || TypeConversionUtil.isAssignable(expectedType, returnType)) {
applicableList.add(method);
possibleClasses.put(containingClass, true);
break;
}
}
}
}
}
final RegisterMethodsProcessor registrar = new RegisterMethodsProcessor();
cache.processMethodsWithName(name, scope, new Processor<PsiMethod>() {
@Override
public boolean process(PsiMethod method) {
ProgressManager.checkCanceled();
if (JavaCompletionUtil.isInExcludedPackage(method, false)
|| !method.hasModifierProperty(PsiModifier.STATIC)) return true;
PsiFile file = method.getContainingFile();
final PsiClass containingClass = method.getContainingClass();
if (file instanceof PsiJavaFile
//do not show methods from default package
&& !((PsiJavaFile)file).getPackageName().isEmpty()) {
if (isEffectivelyDeprecated(method)) {
deprecated.putValue(containingClass, method);
return processCondition();
}
suggestions.putValue(containingClass, method);
}
return processCondition();
}
private boolean isEffectivelyDeprecated(PsiMethod method) {
if (method.isDeprecated()) {
return true;
}
PsiClass aClass = method.getContainingClass();
while (aClass != null) {
if (aClass.isDeprecated()) {
return true;
}
aClass = aClass.getContainingClass();
}
return false;
}
private boolean processCondition() {
return suggestions.size() + deprecated.size() < 50;
}
});
for (Map.Entry<PsiClass, Collection<PsiMethod>> methodEntry : suggestions.entrySet()) {
registrar.registerMethod(methodEntry.getKey(), methodEntry.getValue());
}
for (Map.Entry<PsiClass, Collection<PsiMethod>> deprecatedMethod : deprecated.entrySet()) {
registrar.registerMethod(deprecatedMethod.getKey(), deprecatedMethod.getValue());
}
List<PsiMethod> result = applicableList.isEmpty() ? list : applicableList;
for (int i = result.size() - 1; i >= 0; i--) {
ProgressManager.checkCanceled();
PsiMethod method = result.get(i);
// check for manually excluded
if (isExcluded(method)) {
result.remove(i);
}
}
Collections.sort(result, new PsiProximityComparator(argumentList));
return result;
PsiReferenceExpression reference = element == null ? null : element.getMethodExpression();
String name = reference == null ? null : reference.getReferenceName();
if (name == null) return Collections.emptyList();
final MyStaticMembersProcessor<PsiMethod> processor = new MyStaticMethodProcessor(project, element);
cache.processMethodsWithName(name, element.getResolveScope(), processor);
return processor.getMembersToImport();
}
public static boolean isExcluded(PsiMember method) {
@@ -261,68 +72,51 @@ public class StaticImportMethodFix implements IntentionAction, HintAction {
return name != null && JavaProjectCodeInsightSettings.getSettings(method.getProject()).isExcluded(name);
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) {
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final List<PsiMethod> methodsToImport = getMethodsToImport();
if (methodsToImport.isEmpty()) return;
createQuestionAction(methodsToImport, project, editor).execute();
}
});
}
@NotNull
private StaticImportMethodQuestionAction createQuestionAction(List<PsiMethod> methodsToImport, @NotNull Project project, Editor editor) {
return new StaticImportMethodQuestionAction(project, editor, methodsToImport, myMethodCall);
protected StaticImportMethodQuestionAction<PsiMethod> createQuestionAction(List<PsiMethod> methodsToImport, @NotNull Project project, Editor editor) {
return new StaticImportMethodQuestionAction<PsiMethod>(project, editor, methodsToImport, myMethodCall);
}
private ImportClassFixBase.Result doFix(Editor editor) {
if (candidates.isEmpty()) {
return ImportClassFixBase.Result.POPUP_NOT_SHOWN;
}
final StaticImportMethodQuestionAction action = createQuestionAction(candidates, myMethodCall.getProject(), editor);
@Nullable
@Override
protected PsiElement getElement() {
return myMethodCall.getElement();
}
@Nullable
@Override
protected PsiElement getQualifierExpression() {
final PsiMethodCallExpression element = myMethodCall.getElement();
if (element == null) {
return ImportClassFixBase.Result.POPUP_NOT_SHOWN;
}
if (candidates.size() == 1 && ImportClassFixBase.canAddUnambiguousImport(element.getContainingFile())) {
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@Override
public void run() {
action.execute();
}
});
return ImportClassFixBase.Result.CLASS_AUTO_IMPORTED;
}
String hintText = ShowAutoImportPass.getMessage(candidates.size() > 1, getMethodPresentableText());
if (!ApplicationManager.getApplication().isUnitTestMode() && !HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) {
final TextRange textRange = element.getTextRange();
HintManager.getInstance().showQuestionHint(editor, hintText,
textRange.getStartOffset(),
textRange.getEndOffset(), action);
}
return ImportClassFixBase.Result.POPUP_SHOWN;
return element != null ? element.getMethodExpression().getQualifierExpression() : null;
}
@Nullable
@Override
public boolean startInWriteAction() {
return false;
protected PsiElement resolveRef() {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)getElement();
return methodCallExpression != null ? methodCallExpression.resolveMethod() : null;
}
@Override
public boolean showHint(@NotNull Editor editor) {
final PsiMethodCallExpression callExpression = myMethodCall.getElement();
if (callExpression == null || callExpression.getMethodExpression().getQualifierExpression() != null) {
private static class MyStaticMethodProcessor extends MyStaticMembersProcessor<PsiMethod> {
private MyStaticMethodProcessor(Project project, PsiMethodCallExpression place) {
super(place);
}
@Override
protected boolean isApplicable(PsiMethod method, PsiElement place) {
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(place.getProject()).getResolveHelper();
final PsiExpressionList argumentList = ((PsiMethodCallExpression)place).getArgumentList();
PsiSubstitutor substitutorForMethod = resolveHelper
.inferTypeArguments(method.getTypeParameters(), method.getParameterList().getParameters(),
argumentList.getExpressions(),
PsiSubstitutor.EMPTY, place.getParent(), DefaultParameterTypeInferencePolicy.INSTANCE);
if (PsiUtil.isApplicable(method, substitutorForMethod, argumentList)) {
final PsiType returnType = substitutorForMethod.substitute(method.getReturnType());
final PsiType expectedType = getExpectedType();
return expectedType == null || returnType == null || TypeConversionUtil.isAssignable(expectedType, returnType);
}
return false;
}
ImportClassFixBase.Result result = doFix(editor);
return result == ImportClassFixBase.Result.POPUP_SHOWN || result == ImportClassFixBase.Result.CLASS_AUTO_IMPORTED;
}
}

View File

@@ -19,7 +19,8 @@ import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.actions.AddImportAction;
import com.intellij.codeInsight.hint.QuestionAction;
import com.intellij.codeInsight.intention.impl.AddSingleMemberStaticImportAction;
import com.intellij.ide.util.MethodCellRenderer;
import com.intellij.ide.util.PsiClassListCellRenderer;
import com.intellij.ide.util.PsiElementListCellRenderer;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
@@ -29,7 +30,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.presentation.java.ClassPresentationUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.ui.popup.list.PopupListElementRenderer;
@@ -42,33 +43,38 @@ import javax.swing.*;
import java.awt.*;
import java.util.List;
public class StaticImportMethodQuestionAction implements QuestionAction {
public class StaticImportMethodQuestionAction<T extends PsiMember> implements QuestionAction {
private static final Logger LOG = Logger.getInstance("#" + StaticImportMethodQuestionAction.class.getName());
private final Project myProject;
private final Editor myEditor;
private List<PsiMethod> myCandidates;
private final SmartPsiElementPointer<PsiMethodCallExpression> myMethodCall;
private List<T> myCandidates;
private final SmartPsiElementPointer<? extends PsiElement> myRef;
public StaticImportMethodQuestionAction(Project project,
Editor editor,
List<PsiMethod> candidates,
SmartPsiElementPointer<PsiMethodCallExpression> methodCall) {
List<T> candidates,
SmartPsiElementPointer<? extends PsiElement> ref) {
myProject = project;
myEditor = editor;
myCandidates = candidates;
myMethodCall = methodCall;
myRef = ref;
}
@NotNull
protected String getPopupTitle() {
return QuickFixBundle.message("method.to.import.chooser.title");
}
@Override
public boolean execute() {
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
final PsiMethodCallExpression element = myMethodCall.getElement();
final PsiElement element = myRef.getElement();
if (element == null || !element.isValid()){
return false;
}
for (PsiMethod targetMethod : myCandidates) {
for (T targetMethod : myCandidates) {
if (!targetMethod.isValid()) {
return false;
}
@@ -83,7 +89,7 @@ public class StaticImportMethodQuestionAction implements QuestionAction {
return true;
}
private void doImport(final PsiMethod toImport) {
private void doImport(final T toImport) {
final Project project = toImport.getProject();
CommandProcessor.getInstance().executeCommand(project, new Runnable(){
@Override
@@ -92,7 +98,7 @@ public class StaticImportMethodQuestionAction implements QuestionAction {
@Override
public void run() {
try {
PsiMethodCallExpression element = myMethodCall.getElement();
PsiElement element = myRef.getElement();
if (element != null) {
AddSingleMemberStaticImportAction.bindAllClassRefs(element.getContainingFile(), toImport, toImport.getName(), toImport.getContainingClass());
}
@@ -113,11 +119,11 @@ public class StaticImportMethodQuestionAction implements QuestionAction {
doImport(myCandidates.get(0));
return;
}
final BaseListPopupStep<PsiMethod> step =
new BaseListPopupStep<PsiMethod>(QuickFixBundle.message("method.to.import.chooser.title"), myCandidates) {
final BaseListPopupStep<T> step =
new BaseListPopupStep<T>(getPopupTitle(), myCandidates) {
@Override
public PopupStep onChosen(PsiMethod selectedValue, boolean finalChoice) {
public PopupStep onChosen(T selectedValue, boolean finalChoice) {
if (selectedValue == null) {
return FINAL_CHOICE;
}
@@ -151,18 +157,18 @@ public class StaticImportMethodQuestionAction implements QuestionAction {
}
@Override
public boolean hasSubstep(PsiMethod selectedValue) {
public boolean hasSubstep(T selectedValue) {
return true;
}
@NotNull
@Override
public String getTextFor(PsiMethod value) {
public String getTextFor(T value) {
return ObjectUtils.assertNotNull(value.getName());
}
@Override
public Icon getIconFor(PsiMethod aValue) {
public Icon getIconFor(T aValue) {
return aValue.getIcon(0);
}
};
@@ -171,14 +177,27 @@ public class StaticImportMethodQuestionAction implements QuestionAction {
final PopupListElementRenderer rightArrow = new PopupListElementRenderer(this);
@Override
protected ListCellRenderer getListElementRenderer() {
return new MethodCellRenderer(true, PsiFormatUtilBase.SHOW_NAME){
return new PsiElementListCellRenderer<T>() {
public String getElementText(T element) {
final PsiClass aClass = element.getContainingClass();
LOG.assertTrue(aClass != null);
return ClassPresentationUtil.getNameForClass(aClass, false) + "." + element.getName();
}
public String getContainerText(final T element, final String name) {
return PsiClassListCellRenderer.getContainerTextStatic(element);
}
public int getIconFlags() {
return 0;
}
@Nullable
@Override
protected TextAttributes getNavigationItemAttributes(Object value) {
TextAttributes attrs = super.getNavigationItemAttributes(value);
if (value instanceof PsiMethod && !((PsiMethod)value).isDeprecated()) {
PsiClass psiClass = ((PsiMethod)value).getContainingClass();
if (value instanceof PsiDocCommentOwner && !((PsiDocCommentOwner)value).isDeprecated()) {
PsiClass psiClass = ((T)value).getContainingClass();
if (psiClass != null && psiClass.isDeprecated()) {
return TextAttributes.merge(attrs, super.getNavigationItemAttributes(psiClass));
}

View File

@@ -0,0 +1,17 @@
// "Static import constant..." "true"
package foo;
import static foo.B.aaaaaaa;
public class X {
{
aaaaaaa;
}
}
class B {
public static Integer aaaaaaa = 1;
}
class B1 {
public static String aaaaaaa = "";
}

View File

@@ -0,0 +1,17 @@
// "Static import constant 'foo.B.aaaaaaa'" "true"
package foo;
import static foo.B.aaaaaaa;
public class X {
{
int i = aaaaaaa;
}
}
class B {
public static Integer aaaaaaa = 1;
}
class B1 {
public static String aaaaaaa = "";
}

View File

@@ -0,0 +1,15 @@
// "Static import constant..." "true"
package foo;
public class X {
{
<caret>aaaaaaa;
}
}
class B {
public static Integer aaaaaaa = 1;
}
class B1 {
public static String aaaaaaa = "";
}

View File

@@ -0,0 +1,15 @@
// "Static import constant 'foo.B.aaaaaaa'" "true"
package foo;
public class X {
{
int i = <caret>aaaaaaa;
}
}
class B {
public static Integer aaaaaaa = 1;
}
class B1 {
public static String aaaaaaa = "";
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2000-2016 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;
public class StaticImportConstantTest extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/staticImportConstant";
}
}

View File

@@ -1,6 +1,7 @@
add.import=Add Import
class.to.import.chooser.title=Class to Import
method.to.import.chooser.title=Method to Import
field.to.import.chooser.title=Field to Import
access.static.via.class.reference.family=Access static via class reference
access.static.via.class.reference.text=Access static ''{1}.{0}'' via class ''{2}'' reference
add.default.constructor.family=Add Default Constructor