add quickfix to implement abstract methods for enum constants without class initializer (IDEA-59807)

This commit is contained in:
anna
2010-10-22 21:52:20 +04:00
parent 946b3a3f63
commit 3f0a177532
6 changed files with 129 additions and 65 deletions
@@ -1066,10 +1066,10 @@ public class GenericsHighlightUtil {
}
static void checkEnumConstantForConstructorProblems(PsiEnumConstant enumConstant, final HighlightInfoHolder holder) {
PsiClass containingClass = enumConstant.getContainingClass();
if (enumConstant.getInitializingClass() == null) {
PsiClass containingClass = enumConstant.getContainingClass(); if (enumConstant.getInitializingClass() == null) {
HighlightInfo highlightInfo = HighlightClassUtil.checkInstantiationOfAbstractClass(containingClass, enumConstant.getNameIdentifier());
if (highlightInfo != null) {
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createImplementMethodsFix(enumConstant));
holder.add(highlightInfo);
return;
}
@@ -18,19 +18,33 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.generation.OverrideImplementUtil;
import com.intellij.codeInsight.generation.PsiMethodMember;
import com.intellij.codeInsight.intention.impl.ImplementAbstractMethodHandler;
import com.intellij.codeInspection.IntentionAndQuickFixAction;
import com.intellij.featureStatistics.FeatureUsageTracker;
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.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
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;
public class ImplementMethodsFix extends IntentionAndQuickFixAction {
private final PsiClass myClass;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.TreeMap;
public ImplementMethodsFix(PsiClass aClass) {
myClass = aClass;
public class ImplementMethodsFix extends IntentionAndQuickFixAction {
private final PsiElement myPsiElement;
public ImplementMethodsFix(PsiElement aClass) {
myPsiElement = aClass;
}
@NotNull
@@ -44,12 +58,44 @@ public class ImplementMethodsFix extends IntentionAndQuickFixAction {
}
public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) {
return myClass.isValid() && myClass.getManager().isInProject(myClass);
return myPsiElement.isValid() && myPsiElement.getManager().isInProject(myPsiElement);
}
public void applyFix(final Project project, final PsiFile file, @Nullable final Editor editor) {
if (editor == null || !CodeInsightUtilBase.prepareFileForWrite(myClass.getContainingFile())) return;
OverrideImplementUtil.chooseAndImplementMethods(project, editor, myClass);
if (editor == null || !CodeInsightUtilBase.prepareFileForWrite(myPsiElement.getContainingFile())) return;
if (myPsiElement instanceof PsiEnumConstant) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.overrideimplement");
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());
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 {
final PsiClass psiClass = ImplementAbstractMethodHandler.addClassInitializer((PsiEnumConstant)myPsiElement);
OverrideImplementUtil.overrideOrImplementMethodsInRightPlace(editor, psiClass, selectedElements, chooser.isCopyJavadoc(),
chooser.isInsertOverrideAnnotation());
}
}.execute();
}
else {
OverrideImplementUtil.chooseAndImplementMethods(project, editor, (PsiClass)myPsiElement);
}
}
public boolean startInWriteAction() {
@@ -125,57 +125,9 @@ public class OverrideImplementUtil {
}
}
Map<MethodSignature, CandidateInfo> result = new TreeMap<MethodSignature,CandidateInfo>(new Comparator<MethodSignature>() {
// signatures should appear in the order of declaration
public int compare(MethodSignature o1, MethodSignature o2) {
if (o1 instanceof MethodSignatureBackedByPsiMethod && o2 instanceof MethodSignatureBackedByPsiMethod) {
PsiMethod m1 = ((MethodSignatureBackedByPsiMethod)o1).getMethod();
PsiMethod m2 = ((MethodSignatureBackedByPsiMethod)o2).getMethod();
PsiElement p1 = m1.getParent();
PsiElement p2 = m2.getParent();
if (p1 instanceof PsiClass && p2 instanceof PsiClass) {
final PsiClass c1 = (PsiClass)p1;
final PsiClass c2 = (PsiClass)p2;
if (c1 == c2) {
final List<PsiMethod> methods = Arrays.asList(c1.getMethods());
return methods.indexOf(m1) - methods.indexOf(m2);
}
if (c1.isInheritor(c2, true)) return -1;
if (c2.isInheritor(c1, true)) return 1;
return StringUtil.notNullize(c1.getName()).compareTo(StringUtil.notNullize(c2.getName()));
}
return m1.getTextOffset() - m2.getTextOffset();
}
return 0;
}
});
final Map<MethodSignature, CandidateInfo> result = new TreeMap<MethodSignature,CandidateInfo>(new MethodSignatureComparator());
if (toImplement || aClass.isInterface()) {
for (Map.Entry<MethodSignature, PsiMethod> entry : abstracts.entrySet()) {
MethodSignature signature = entry.getKey();
PsiMethod abstractOne = entry.getValue();
PsiMethod concrete = concretes.get(signature);
if (concrete == null
|| PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList())
|| !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) {
if (finals.get(signature) == null) {
PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne, signature.getSubstitutor());
CandidateInfo info = new CandidateInfo(abstractOne, subst);
result.put(signature, info);
}
}
}
for (final MethodImplementor implementor : getImplementors()) {
for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) {
MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(),
method.getTypeParameterList(), PsiSubstitutor.EMPTY);
CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY);
result.put(signature, info);
}
}
collectMethodsToImplement(aClass, abstracts, finals, concretes, result);
}
else {
for (Map.Entry<MethodSignature, PsiMethod> entry : concretes.entrySet()) {
@@ -196,6 +148,36 @@ public class OverrideImplementUtil {
return result;
}
public static void collectMethodsToImplement(PsiClass aClass,
Map<MethodSignature, PsiMethod> abstracts,
Map<MethodSignature, PsiMethod> finals,
Map<MethodSignature, PsiMethod> concretes,
Map<MethodSignature, CandidateInfo> result) {
for (Map.Entry<MethodSignature, PsiMethod> entry : abstracts.entrySet()) {
MethodSignature signature = entry.getKey();
PsiMethod abstractOne = entry.getValue();
PsiMethod concrete = concretes.get(signature);
if (concrete == null
|| PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList())
|| !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) {
if (finals.get(signature) == null) {
PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne, signature.getSubstitutor());
CandidateInfo info = new CandidateInfo(abstractOne, subst);
result.put(signature, info);
}
}
}
for (final MethodImplementor implementor : getImplementors()) {
for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) {
MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(),
method.getTypeParameterList(), PsiSubstitutor.EMPTY);
CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY);
result.put(signature, info);
}
}
}
private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) {
if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true;
if (!left.getContainingClass().isInterface()) return true;
@@ -497,7 +479,7 @@ public class OverrideImplementUtil {
@Nullable
public static MemberChooser<PsiMethodMember> showOverrideImplementChooser(Editor editor,
final PsiClass aClass,
final PsiElement aClass,
final boolean toImplement,
Collection<CandidateInfo> candidates,
Collection<CandidateInfo> secondary) {
@@ -568,7 +550,7 @@ public class OverrideImplementUtil {
});
}
private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiClass aClass,
private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiElement aClass,
final boolean toImplement,
final MemberChooser<PsiMethodMember> chooser) {
final JComponent preferredFocusedComponent = chooser.getPreferredFocusedComponent();
@@ -725,4 +707,32 @@ public class OverrideImplementUtil {
boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
return overrideOrImplementMethodCandidates(aClass, candidatesToImplement, copyJavadoc, insert);
}
public static class MethodSignatureComparator implements Comparator<MethodSignature> {
// signatures should appear in the order of declaration
public int compare(MethodSignature o1, MethodSignature o2) {
if (o1 instanceof MethodSignatureBackedByPsiMethod && o2 instanceof MethodSignatureBackedByPsiMethod) {
PsiMethod m1 = ((MethodSignatureBackedByPsiMethod)o1).getMethod();
PsiMethod m2 = ((MethodSignatureBackedByPsiMethod)o2).getMethod();
PsiElement p1 = m1.getParent();
PsiElement p2 = m2.getParent();
if (p1 instanceof PsiClass && p2 instanceof PsiClass) {
final PsiClass c1 = (PsiClass)p1;
final PsiClass c2 = (PsiClass)p2;
if (c1 == c2) {
final List<PsiMethod> methods = Arrays.asList(c1.getMethods());
return methods.indexOf(m1) - methods.indexOf(m2);
}
if (c1.isInheritor(c2, true)) return -1;
if (c2.isInheritor(c1, true)) return 1;
return StringUtil.notNullize(c1.getName()).compareTo(StringUtil.notNullize(c2.getName()));
}
return m1.getTextOffset() - m2.getTextOffset();
}
return 0;
}
}
}
@@ -162,7 +162,7 @@ public class ImplementAbstractMethodHandler {
}, CodeInsightBundle.message("intention.implement.abstract.method.command.name"), null);
}
static PsiClass addClassInitializer(PsiEnumConstant enumConstant) {
public static PsiClass addClassInitializer(PsiEnumConstant enumConstant) {
final PsiEnumConstant constantFromText = JavaPsiFacade.getElementFactory(enumConstant.getProject()).createEnumConstantFromText(
enumConstant.getName() + "{}", enumConstant);
final PsiEnumConstant replace = (PsiEnumConstant)enumConstant.replace(constantFromText);
@@ -60,6 +60,10 @@ public class QuickFixFactoryImpl extends QuickFixFactory {
return new ImplementMethodsFix(aClass);
}
public IntentionAction createImplementMethodsFix(@NotNull PsiElement psiElement) {
return new ImplementMethodsFix(psiElement);
}
public IntentionAction createMethodThrowsFix(@NotNull PsiMethod method,
@NotNull PsiClassType exceptionClass,
boolean shouldThrow,
@@ -42,7 +42,11 @@ public abstract class QuickFixFactory {
public abstract IntentionAction createAddMethodFix(@NotNull PsiMethod method, @NotNull PsiClass toClass);
public abstract IntentionAction createAddMethodFix(@NotNull String methodText, @NotNull PsiClass toClass, String... exceptions);
public abstract IntentionAction createImplementMethodsFix(@NotNull PsiClass aClass);
/**
* @param psiElement psiClass or enum constant without class initializer
*/
public abstract IntentionAction createImplementMethodsFix(@NotNull PsiElement psiElement);
public abstract IntentionAction createImplementMethodsFix(@NotNull PsiClass psiElement);
public abstract IntentionAction createMethodThrowsFix(@NotNull PsiMethod method, @NotNull PsiClassType exceptionClass, boolean shouldThrow, boolean showContainingClass);
public abstract IntentionAction createAddDefaultConstructorFix(@NotNull PsiClass aClass);
public abstract IntentionAction createMethodParameterTypeFix(@NotNull PsiMethod method, int index, @NotNull PsiType newType, boolean fixWholeHierarchy);