expand implement methods fix availability for new Runnable()<caret> (IDEA-74043 )

This commit is contained in:
anna
2011-10-07 14:11:21 +02:00
parent 0a9507dcdc
commit 9188075b91
7 changed files with 127 additions and 21 deletions
@@ -22,15 +22,22 @@
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.ClassUtil;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.RefCountHolder;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.generation.OverrideImplementUtil;
import com.intellij.codeInsight.generation.PsiMethodMember;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.ide.util.MemberChooser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
@@ -118,12 +125,21 @@ public class HighlightClassUtil {
if (aClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
String baseClassName = aClass.getName();
String message = JavaErrorMessages.message("abstract.cannot.be.instantiated", baseClassName);
errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, highlightElement, message);
if (!aClass.isInterface() && ClassUtil.getAnyAbstractMethod(aClass) == null) {
PsiElement parent = highlightElement.getParent();
if (parent instanceof PsiNewExpression) {
errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parent, message);
} else {
errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, highlightElement, message);
}
final PsiMethod anyAbstractMethod = ClassUtil.getAnyAbstractMethod(aClass);
if (!aClass.isInterface() && anyAbstractMethod == null) {
// suggest to make not abstract only if possible
IntentionAction fix = QUICK_FIX_FACTORY.createModifierListFix(aClass, PsiModifier.ABSTRACT, false, false);
QuickFixAction.registerQuickFixAction(errorResult, fix);
}
if (anyAbstractMethod != null && parent instanceof PsiNewExpression) {
QuickFixAction.registerQuickFixAction(errorResult, new ImplementAbstractClassMethodsFix(parent));
}
}
return errorResult;
}
@@ -863,4 +879,49 @@ public class HighlightClassUtil {
return null;
}
private static class ImplementAbstractClassMethodsFix extends ImplementMethodsFix {
public ImplementAbstractClassMethodsFix(PsiElement highlightElement) {
super(highlightElement);
}
@Override
public boolean isAvailable(@NotNull Project project,
@NotNull PsiFile file,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
return startElement instanceof PsiNewExpression;
}
@Override
public void invoke(@NotNull final Project project,
@NotNull PsiFile file,
@Nullable("is null when called from inspection") final Editor editor,
@NotNull final PsiElement startElement,
@NotNull PsiElement endElement) {
final PsiFile containingFile = startElement.getContainingFile();
if (editor == null || !CodeInsightUtilBase.prepareFileForWrite(containingFile)) return;
PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)startElement).getClassReference();
final MemberChooser<PsiMethodMember> chooser = chooseMethodsToImplement(editor, startElement, (PsiClass)classReference.resolve());
if (chooser == null) return;
final List<PsiMethodMember> selectedElements = chooser.getSelectedElements();
if (selectedElements == null || selectedElements.isEmpty()) return;
new WriteCommandAction(project, file) {
protected void run(final Result result) throws Throwable {
PsiNewExpression newExpression =
(PsiNewExpression)JavaPsiFacade.getElementFactory(project).createExpressionFromText(startElement.getText() + "{}", startElement);
newExpression = (PsiNewExpression)startElement.replace(newExpression);
final PsiClass psiClass = newExpression.getAnonymousClass();
PsiSubstitutor superClassSubstitutor = TypeConversionUtil
.getSuperClassSubstitutor(((PsiAnonymousClass)psiClass).getBaseClassType().resolve(), psiClass, PsiSubstitutor.EMPTY);
for (PsiMethodMember selectedElement : selectedElements) {
selectedElement.setSubstitutor(superClassSubstitutor);
}
OverrideImplementUtil.overrideOrImplementMethodsInRightPlace(editor, psiClass, selectedElements, chooser.isCopyJavadoc(),
chooser.isInsertOverrideAnnotation());
}
}.execute();
}
}
}
@@ -30,11 +30,11 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.util.MethodSignature;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.TreeMap;
@@ -73,21 +73,7 @@ public class ImplementMethodsFix extends LocalQuickFixAndIntentionActionOnPsiEle
if (editor == null || !CodeInsightUtilBase.prepareFileForWrite(myPsiElement.getContainingFile())) return;
if (myPsiElement instanceof PsiEnumConstant) {
FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT);
final TreeMap<MethodSignature, CandidateInfo> result =
new TreeMap<MethodSignature, CandidateInfo>(new OverrideImplementUtil.MethodSignatureComparator());
final HashMap<MethodSignature, PsiMethod> abstracts = new HashMap<MethodSignature, PsiMethod>();
for (PsiMethod method : ((PsiEnumConstant)myPsiElement).getContainingClass().getMethods()) {
if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
abstracts.put(method.getHierarchicalMethodSignature(), method);
}
}
final HashMap<MethodSignature, PsiMethod> finals = new HashMap<MethodSignature, PsiMethod>();
final HashMap<MethodSignature, PsiMethod> concretes = new HashMap<MethodSignature, PsiMethod>();
OverrideImplementUtil.collectMethodsToImplement(null, abstracts, finals, concretes, result);
final MemberChooser<PsiMethodMember> chooser =
OverrideImplementUtil.showOverrideImplementChooser(editor, myPsiElement, true, result.values(), Collections.<CandidateInfo>emptyList());
final MemberChooser<PsiMethodMember> chooser = chooseMethodsToImplement(editor, startElement, ((PsiEnumConstant)myPsiElement).getContainingClass());
if (chooser == null) return;
final List<PsiMethodMember> selectedElements = chooser.getSelectedElements();
@@ -111,4 +97,26 @@ public class ImplementMethodsFix extends LocalQuickFixAndIntentionActionOnPsiEle
return false;
}
@Nullable
protected static MemberChooser<PsiMethodMember> chooseMethodsToImplement(Editor editor, PsiElement startElement, PsiClass aClass) {
FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT);
final TreeMap<MethodSignature, CandidateInfo> result =
new TreeMap<MethodSignature, CandidateInfo>(new OverrideImplementUtil.MethodSignatureComparator());
final HashMap<MethodSignature, PsiMethod> abstracts = new HashMap<MethodSignature, PsiMethod>();
final HashMap<MethodSignature, PsiMethod> finals = new HashMap<MethodSignature, PsiMethod>();
final HashMap<MethodSignature, PsiMethod> concretes = new HashMap<MethodSignature, PsiMethod>();
for (PsiMethod method : aClass.getMethods()) {
if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
abstracts.put(method.getHierarchicalMethodSignature(), method);
}
}
OverrideImplementUtil.collectMethodsToImplement(null, abstracts, finals, concretes, result);
return OverrideImplementUtil
.showOverrideImplementChooser(editor, startElement, true, result.values(), Collections.<CandidateInfo>emptyList());
}
}
@@ -26,7 +26,7 @@ import com.intellij.psi.util.PsiFormatUtil;
*/
public abstract class PsiElementClassMember<T extends PsiDocCommentOwner> extends PsiDocCommentOwnerMemberChooserObject implements ClassMemberWithElement {
private final T myPsiMember;
private final PsiSubstitutor mySubstitutor;
private PsiSubstitutor mySubstitutor;
protected PsiElementClassMember(final T psiMember, String text) {
this(psiMember, PsiSubstitutor.EMPTY, text);
@@ -46,6 +46,10 @@ public abstract class PsiElementClassMember<T extends PsiDocCommentOwner> extend
return mySubstitutor;
}
public void setSubstitutor(PsiSubstitutor substitutor) {
mySubstitutor = substitutor;
}
public MemberChooserObject getParentNodeDelegate() {
final PsiClass psiClass = getContainingClass();
final String text = PsiFormatUtil.formatClass(psiClass, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_FQ_NAME);
@@ -1,9 +1,9 @@
// instantiate abstract
public class a {
void f() {
new <error descr="'ii' is abstract; cannot be instantiated">ii</error>();
<error descr="'ii' is abstract; cannot be instantiated">new ii()</error>;
new <error descr="'c1' is abstract; cannot be instantiated">c1</error>();
<error descr="'c1' is abstract; cannot be instantiated">new c1()</error>;
new <error descr="Class 'Anonymous class derived from c1' must either be declared abstract or implement abstract method 'f1(int)' in 'c1'">c1</error>() {
public void f2() {}
@@ -0,0 +1,9 @@
// "Implement Methods" "true"
class c {
void foo() {
new I<String>()<caret>
}
}
interface I<T> {
foo(T t);
}
@@ -0,0 +1,6 @@
// "Implement Methods" "true"
class c {
void foo() {
new Runnable()<caret>
}
}
@@ -0,0 +1,18 @@
package com.intellij.codeInsight;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixAvailabilityTestCase;
/**
* User: anna
* Date: 10/7/11
*/
public class ImplementAbstractFromNewTest extends LightQuickFixAvailabilityTestCase {
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/implementAbstractFromNew";
}
}