select all methods in Implement Methods Quick Fix dialog
[fedora-idea.git] / codeInsight / impl / com / intellij / codeInsight / generation / OverrideImplementUtil.java
blob8395913cb61c9d0e1f21302e68164c077f64cfb9
1 package com.intellij.codeInsight.generation;
3 import com.intellij.codeInsight.AnnotationUtil;
4 import com.intellij.codeInsight.CodeInsightActionHandler;
5 import com.intellij.codeInsight.CodeInsightBundle;
6 import com.intellij.codeInsight.MethodImplementor;
7 import com.intellij.codeInsight.intention.AddAnnotationFix;
8 import com.intellij.featureStatistics.FeatureUsageTracker;
9 import com.intellij.ide.fileTemplates.FileTemplate;
10 import com.intellij.ide.fileTemplates.FileTemplateManager;
11 import com.intellij.ide.fileTemplates.FileTemplateUtil;
12 import com.intellij.ide.fileTemplates.JavaTemplateUtil;
13 import com.intellij.ide.util.MemberChooser;
14 import com.intellij.ide.util.PropertiesComponent;
15 import com.intellij.openapi.actionSystem.*;
16 import com.intellij.openapi.application.ApplicationManager;
17 import com.intellij.openapi.application.Result;
18 import com.intellij.openapi.command.WriteCommandAction;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.editor.ScrollType;
22 import com.intellij.openapi.extensions.Extensions;
23 import com.intellij.openapi.fileEditor.FileEditorManager;
24 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
25 import com.intellij.openapi.fileTypes.FileType;
26 import com.intellij.openapi.fileTypes.FileTypeManager;
27 import com.intellij.openapi.keymap.Keymap;
28 import com.intellij.openapi.keymap.KeymapManager;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.ui.DialogWrapper;
31 import com.intellij.openapi.ui.Messages;
32 import com.intellij.openapi.util.Ref;
33 import com.intellij.psi.*;
34 import com.intellij.psi.codeStyle.CodeStyleManager;
35 import com.intellij.psi.codeStyle.CodeStyleSettings;
36 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
37 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
38 import com.intellij.psi.impl.source.jsp.jspJava.JspClass;
39 import com.intellij.psi.infos.CandidateInfo;
40 import com.intellij.psi.javadoc.PsiDocComment;
41 import com.intellij.psi.util.*;
42 import com.intellij.util.ArrayUtil;
43 import com.intellij.util.Function;
44 import com.intellij.util.IncorrectOperationException;
45 import com.intellij.util.containers.ContainerUtil;
46 import com.intellij.util.containers.HashMap;
47 import org.jetbrains.annotations.NonNls;
48 import org.jetbrains.annotations.NotNull;
49 import org.jetbrains.annotations.Nullable;
51 import javax.swing.*;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.KeyEvent;
54 import java.awt.event.InputEvent;
55 import java.util.*;
57 public class OverrideImplementUtil {
58 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.OverrideImplementUtil");
60 @NonNls private static final String PROP_COMBINED_OVERRIDE_IMPLEMENT = "OverrideImplement.combined";
62 private OverrideImplementUtil() {
65 @NotNull
66 public static Collection<CandidateInfo> getMethodsToOverrideImplement(PsiClass aClass, boolean toImplement) {
67 return getMapToOverrideImplement(aClass, toImplement).values();
70 @NotNull
71 public static Collection<MethodSignature> getMethodSignaturesToImplement(@NotNull PsiClass aClass) {
72 return getMapToOverrideImplement(aClass, true).keySet();
75 @NotNull
76 public static Collection<MethodSignature> getMethodSignaturesToOverride(@NotNull PsiClass aClass) {
77 return getMapToOverrideImplement(aClass, false).keySet();
80 @NotNull
81 private static Map<MethodSignature, CandidateInfo> getMapToOverrideImplement(PsiClass aClass, boolean toImplement) {
82 final PsiSubstitutor contextSubstitutor = getContextSubstitutor(aClass);
83 Map<MethodSignature, PsiMethod> abstracts = new LinkedHashMap<MethodSignature,PsiMethod>();
84 Map<MethodSignature, PsiMethod> finals = new HashMap<MethodSignature,PsiMethod>();
85 Map<MethodSignature, PsiMethod> concretes = new LinkedHashMap<MethodSignature,PsiMethod>();
86 Map<PsiClass, PsiSubstitutor> substitutors = new HashMap<PsiClass,PsiSubstitutor>();
88 PsiMethod[] allMethods = aClass.getAllMethods();
89 PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
90 for (PsiMethod method : allMethods) {
91 if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) continue;
92 PsiClass hisClass = method.getContainingClass();
93 //Filter non-immediate super constructors
94 if (method.isConstructor() && (!aClass.isInheritor(hisClass, false) || aClass instanceof PsiAnonymousClass || aClass.isEnum())) {
95 continue;
98 PsiSubstitutor substitutor;
99 if ((substitutor = substitutors.get(hisClass)) == null) {
100 substitutor = aClass.isInheritor(hisClass, true) ?
101 TypeConversionUtil.getSuperClassSubstitutor(hisClass, aClass, PsiSubstitutor.EMPTY) : PsiSubstitutor.EMPTY;
102 substitutor = substitutor.putAll(contextSubstitutor);
103 substitutors.put(hisClass, substitutor);
106 String name = method.isConstructor() ? aClass.getName() : method.getName();
107 substitutor = GenerateMembersUtil.correctSubstitutor(method, substitutor);
109 PsiTypeParameterList typeParameterList = PsiUtil.isRawSubstitutor(method, substitutor) ? null : method.getTypeParameterList();
111 MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, method.getParameterList(), typeParameterList,
112 substitutor);
113 if (MethodSignatureUtil.findMethodBySignature(aClass, signature, false) != null) continue;
115 if (method.hasModifierProperty(PsiModifier.FINAL)) {
116 finals.put(signature, method);
117 continue;
120 Map<MethodSignature, PsiMethod> map = hisClass.isInterface() || method.hasModifierProperty(PsiModifier.ABSTRACT) ? abstracts : concretes;
121 PsiMethod other = map.get(signature);
122 if (other == null || preferLeftForImplement(method, other)) {
123 map.put(signature, method);
127 Map<MethodSignature, CandidateInfo> result = new LinkedHashMap<MethodSignature,CandidateInfo>();
128 if (toImplement || aClass.isInterface()) {
129 for (Map.Entry<MethodSignature, PsiMethod> entry : abstracts.entrySet()) {
130 MethodSignature signature = entry.getKey();
131 PsiMethod abstractOne = entry.getValue();
132 PsiMethod concrete = concretes.get(signature);
133 if (concrete == null
134 || PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList())
135 || !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) {
136 if (finals.get(signature) == null) {
137 PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne,
138 substitutors.get(abstractOne.getContainingClass()));
139 CandidateInfo info = new CandidateInfo(abstractOne, subst);
140 result.put(signature, info);
145 for (final MethodImplementor implementor : getImplementors()) {
146 for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) {
147 MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(),
148 method.getTypeParameterList(), PsiSubstitutor.EMPTY);
149 CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY);
150 result.put(signature, info);
154 else {
155 for (Map.Entry<MethodSignature, PsiMethod> entry : concretes.entrySet()) {
156 MethodSignature signature = entry.getKey();
157 PsiMethod concrete = entry.getValue();
158 if (finals.get(signature) == null) {
159 PsiMethod abstractOne = abstracts.get(signature);
160 if (abstractOne == null || !abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true) ||
161 CommonClassNames.JAVA_LANG_OBJECT.equals(concrete.getContainingClass().getQualifiedName())) {
162 PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(concrete, substitutors.get(concrete.getContainingClass()));
163 CandidateInfo info = new CandidateInfo(concrete, subst);
164 result.put(signature, info);
170 return result;
173 private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) {
174 if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true;
175 // implement annotated method
176 PsiAnnotation[] leftAnnotations = left.getModifierList().getAnnotations();
177 PsiAnnotation[] rightAnnotations = right.getModifierList().getAnnotations();
178 return leftAnnotations.length > rightAnnotations.length;
181 private static MethodImplementor[] getImplementors() {
182 return Extensions.getExtensions(MethodImplementor.EXTENSION_POINT_NAME);
186 * generate methods (with bodies) corresponding to given method declaration
187 * there are maybe two method implementations for one declaration
188 * (e.g. EJB' create() -> ejbCreate(), ejbPostCreate() )
189 * @param aClass context for method implementations
190 * @param method method to override or implement
191 * @param toCopyJavaDoc true if copy JavaDoc from method declaration
192 * @return list of method prototypes
194 @NotNull
195 public static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass, PsiMethod method, boolean toCopyJavaDoc) throws IncorrectOperationException {
196 final PsiClass containingClass = method.getContainingClass();
197 PsiSubstitutor substitutor = aClass.isInheritor(containingClass, true) ?
198 TypeConversionUtil.getSuperClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY) : PsiSubstitutor.EMPTY;
199 return overrideOrImplementMethod(aClass, method, substitutor, toCopyJavaDoc, true);
202 private static boolean isInsertOverride(PsiMethod superMethod, PsiClass targetClass) {
203 if (!CodeStyleSettingsManager.getSettings(targetClass.getProject()).INSERT_OVERRIDE_ANNOTATION || !PsiUtil.isLanguageLevel5OrHigher(targetClass)) {
204 return false;
206 if (PsiUtil.isLanguageLevel6OrHigher(targetClass)) return true;
207 if (targetClass.isInterface()) return true;
208 PsiClass superClass = superMethod.getContainingClass();
209 return !superClass.isInterface();
212 @NotNull
213 private static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass,
214 PsiMethod method,
215 PsiSubstitutor substitutor,
216 boolean toCopyJavaDoc,
217 boolean insertAtOverrideIfPossible) throws IncorrectOperationException {
218 if (!method.isValid() || !substitutor.isValid()) return Collections.emptyList();
220 List<PsiMethod> results = new ArrayList<PsiMethod>();
221 for (final MethodImplementor implementor : getImplementors()) {
222 results.addAll(Arrays.asList(implementor.createImplementationPrototypes(aClass, method)));
224 if (results.isEmpty()) {
225 PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor);
227 PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
228 PsiMethod result = (PsiMethod)factory.createClass("Dummy").add(method1);
229 if (result instanceof PsiAnnotationMethod) {
230 PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)result).getDefaultValue();
231 if (defaultValue != null) {
232 PsiElement defaultKeyword = defaultValue;
233 while (!(defaultKeyword instanceof PsiKeyword) && defaultKeyword != null) {
234 defaultKeyword = defaultKeyword.getPrevSibling();
236 if (defaultKeyword == null) defaultKeyword = defaultValue;
237 defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue);
240 results.add(result);
243 for (Iterator<PsiMethod> iterator = results.iterator(); iterator.hasNext();) {
244 PsiMethod result = iterator.next();
245 PsiUtil.setModifierProperty(result, PsiModifier.ABSTRACT, aClass.isInterface());
246 PsiUtil.setModifierProperty(result, PsiModifier.NATIVE, false);
248 if (!toCopyJavaDoc){
249 PsiDocComment comment = result.getDocComment();
250 if (comment != null){
251 comment.delete();
255 if (insertAtOverrideIfPossible && isInsertOverride(method, aClass) && !method.isConstructor()) {
256 annotate(result, "java.lang.Override");
259 for (OverrideImplementsAnnotationsHandler annotationsHandler : Extensions
260 .getExtensions(OverrideImplementsAnnotationsHandler.EP_NAME)) {
261 for (String annotationFQName : annotationsHandler.getAnnotations()) {
262 if (AnnotationUtil.isAnnotated(method, annotationFQName, false)) {
263 annotate(result, annotationFQName, annotationsHandler.annotationsToRemove(annotationFQName));
268 final PsiCodeBlock body = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createCodeBlockFromText("{}", null);
269 PsiCodeBlock oldbody = result.getBody();
270 if (oldbody != null){
271 oldbody.replace(body);
273 else{
274 result.add(body);
277 setupMethodBody(result, method, aClass);
279 // probably, it's better to reformat the whole method - it can go from other style sources
280 final Project project = method.getProject();
281 CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
282 CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project);
283 boolean keepBreaks = settings.KEEP_LINE_BREAKS;
284 settings.KEEP_LINE_BREAKS = false;
285 result = (PsiMethod)JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);
286 result = (PsiMethod)codeStyleManager.reformat(result);
287 settings.KEEP_LINE_BREAKS = keepBreaks;
289 if (aClass.findMethodBySignature(result, false) != null) {
290 iterator.remove();
294 return results;
297 private static void annotate(final PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException {
298 Project project = result.getProject();
299 AddAnnotationFix fix = new AddAnnotationFix(fqn, result, annosToRemove);
300 if (fix.isAvailable(project, null, result.getContainingFile())) {
301 fix.invoke(project, null, result.getContainingFile());
305 public static boolean isOverridable(PsiMethod method) {
306 return !method.isConstructor()
307 && !method.hasModifierProperty(PsiModifier.STATIC)
308 && !method.hasModifierProperty(PsiModifier.FINAL)
309 && !method.hasModifierProperty(PsiModifier.PRIVATE);
312 @NotNull
313 public static List<PsiGenerationInfo<PsiMethod>> overrideOrImplementMethods(PsiClass aClass,
314 Collection<PsiMethodMember> candidates,
315 boolean toCopyJavaDoc,
316 boolean toInsertAtOverride)
317 throws IncorrectOperationException {
318 List<CandidateInfo> candidateInfos = ContainerUtil.map2List(candidates, new Function<PsiMethodMember, CandidateInfo>() {
319 public CandidateInfo fun(final PsiMethodMember s) {
320 return new CandidateInfo(s.getElement(), s.getSubstitutor());
323 final List<PsiMethod> methods = overrideOrImplementMethodCandidates(aClass, candidateInfos, toCopyJavaDoc, toInsertAtOverride);
324 return convert2GenerationInfos(methods);
327 @NotNull
328 public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass,
329 Collection<CandidateInfo> candidates,
330 boolean toCopyJavaDoc,
331 boolean insertOverrideWherePossible) throws IncorrectOperationException {
332 List<PsiMethod> result = new ArrayList<PsiMethod>();
333 for (CandidateInfo candidateInfo : candidates) {
334 result.addAll(overrideOrImplementMethod(aClass, (PsiMethod)candidateInfo.getElement(), candidateInfo.getSubstitutor(),
335 toCopyJavaDoc, insertOverrideWherePossible));
337 return result;
340 public static List<PsiGenerationInfo<PsiMethod>> convert2GenerationInfos(final Collection<PsiMethod> methods) {
341 return ContainerUtil.map2List(methods, new Function<PsiMethod, PsiGenerationInfo<PsiMethod>>() {
342 public PsiGenerationInfo<PsiMethod> fun(final PsiMethod s) {
343 return new PsiGenerationInfo<PsiMethod>(s);
348 @NotNull
349 private static String callSuper (PsiMethod superMethod, PsiMethod overriding) {
350 @NonNls StringBuilder buffer = new StringBuilder();
351 if (!superMethod.isConstructor() && superMethod.getReturnType() != PsiType.VOID) {
352 buffer.append("return ");
354 buffer.append("super");
355 PsiParameter[] parms = overriding.getParameterList().getParameters();
356 if (!superMethod.isConstructor()){
357 buffer.append(".");
358 buffer.append(superMethod.getName());
360 buffer.append("(");
361 for (int i = 0; i < parms.length; i++) {
362 String name = parms[i].getName();
363 if (i > 0) buffer.append(",");
364 buffer.append(name);
366 buffer.append(")");
367 return buffer.toString();
370 public static void setupMethodBody(PsiMethod result, PsiMethod originalMethod, PsiClass targetClass) throws IncorrectOperationException {
371 String templName = originalMethod.hasModifierProperty(PsiModifier.ABSTRACT) ?
372 JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY;
373 FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName);
374 setupMethodBody(result, originalMethod, targetClass, template);
377 public static void setupMethodBody(final PsiMethod result, final PsiMethod originalMethod, final PsiClass targetClass,
378 final FileTemplate template) throws IncorrectOperationException {
379 if (targetClass.isInterface()) {
380 final PsiCodeBlock body = result.getBody();
381 if (body != null) body.delete();
384 FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(template.getExtension());
385 PsiType returnType = result.getReturnType();
386 if (returnType == null) {
387 returnType = PsiType.VOID;
389 Properties properties = new Properties();
390 properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnType.getPresentableText());
391 properties.setProperty(FileTemplate.ATTRIBUTE_DEFAULT_RETURN_VALUE, PsiTypesUtil.getDefaultValueOfType(returnType));
392 properties.setProperty(FileTemplate.ATTRIBUTE_CALL_SUPER, callSuper(originalMethod, result));
393 JavaTemplateUtil.setClassAndMethodNameProperties(properties, targetClass, result);
395 PsiElementFactory factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory();
396 @NonNls String methodText;
397 try {
398 String bodyText = template.getText(properties);
399 if (!"".equals(bodyText)) bodyText += "\n";
400 methodText = "void foo () {\n" + bodyText + "}";
401 methodText = FileTemplateUtil.indent(methodText, result.getProject(), fileType);
402 } catch (Exception e) {
403 throw new IncorrectOperationException("Failed to parse file template",e);
405 if (methodText != null) {
406 PsiMethod m;
407 try {
408 m = factory.createMethodFromText(methodText, originalMethod);
410 catch (IncorrectOperationException e) {
411 ApplicationManager.getApplication().invokeLater(new Runnable() {
412 public void run() {
413 Messages.showErrorDialog(CodeInsightBundle.message("override.implement.broken.file.template.message"),
414 CodeInsightBundle.message("override.implement.broken.file.template.title"));
417 return;
419 PsiCodeBlock oldBody = result.getBody();
420 if (oldBody != null) {
421 oldBody.replace(m.getBody());
426 public static void chooseAndOverrideMethods(Project project, Editor editor, PsiClass aClass){
427 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.overrideimplement");
428 chooseAndOverrideOrImplementMethods(project, editor, aClass, false);
431 public static void chooseAndImplementMethods(Project project, Editor editor, PsiClass aClass){
432 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.overrideimplement");
433 chooseAndOverrideOrImplementMethods(project, editor, aClass, true);
436 private static void chooseAndOverrideOrImplementMethods(final Project project,
437 final Editor editor,
438 final PsiClass aClass,
439 final boolean toImplement){
440 LOG.assertTrue(aClass.isValid());
441 ApplicationManager.getApplication().assertReadAccessAllowed();
443 Collection<CandidateInfo> candidates = getMethodsToOverrideImplement(aClass, toImplement);
444 Collection<CandidateInfo> secondary = toImplement ? Collections.<CandidateInfo>emptyList() : getMethodsToOverrideImplement(aClass, true);
446 if (candidates.isEmpty() && secondary.isEmpty()) return;
448 final PsiMethodMember[] onlyPrimary = convertToMethodMembers(candidates);
449 final PsiMethodMember[] all = ArrayUtil.mergeArrays(onlyPrimary, convertToMethodMembers(secondary), PsiMethodMember.class);
451 final String toMerge = PropertiesComponent.getInstance(project).getValue(PROP_COMBINED_OVERRIDE_IMPLEMENT);
452 final Ref<Boolean> merge = Ref.create(!"false".equals(toMerge));
454 final boolean isAll = merge.get().booleanValue();
455 final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(isAll ? all : onlyPrimary, false, true, project,
456 PsiUtil.isLanguageLevel5OrHigher(aClass)) {
458 @Override
459 protected void fillToolbarActions(DefaultActionGroup group) {
460 super.fillToolbarActions(group);
461 if (toImplement) return;
463 final ToggleAction mergeAction = new ToggleAction("Show methods to implement") {
464 @Override
465 public boolean isSelected(AnActionEvent e) {
466 return merge.get().booleanValue();
469 @Override
470 public void setSelected(AnActionEvent e, boolean state) {
471 merge.set(state);
472 resetElements(state ? all : onlyPrimary);
473 setTitle(getChooserTitle(false, merge));
476 mergeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.ALT_MASK)), myTree);
477 group.add(mergeAction);
480 chooser.setTitle(getChooserTitle(toImplement, merge));
481 registerHandlerForComplementaryAction(project, editor, aClass, toImplement, chooser);
483 chooser.setCopyJavadocVisible(true);
484 chooser.selectElements(isAll ? all : onlyPrimary);
485 chooser.show();
486 if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return;
488 PropertiesComponent.getInstance(project).setValue(PROP_COMBINED_OVERRIDE_IMPLEMENT, merge.get().toString());
490 final List<PsiMethodMember> selectedElements = chooser.getSelectedElements();
491 if (selectedElements == null || selectedElements.isEmpty()) return;
493 new WriteCommandAction(project, aClass.getContainingFile()) {
494 protected void run(final Result result) throws Throwable {
495 overrideOrImplementMethodsInRightPlace(editor, aClass, selectedElements, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation());
497 }.execute();
500 private static String getChooserTitle(boolean toImplement, Ref<Boolean> merge) {
501 return toImplement
502 ? CodeInsightBundle.message("methods.to.implement.chooser.title")
503 : merge.get().booleanValue()
504 ? CodeInsightBundle.message("methods.to.override.implement.chooser.title")
505 : CodeInsightBundle.message("methods.to.override.chooser.title");
508 private static PsiMethodMember[] convertToMethodMembers(Collection<CandidateInfo> candidates) {
509 return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function<CandidateInfo, PsiMethodMember>() {
510 public PsiMethodMember fun(final CandidateInfo s) {
511 return new PsiMethodMember(s);
516 private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiClass aClass,
517 final boolean toImplement,
518 final MemberChooser<PsiMethodMember> chooser) {
519 final JComponent preferredFocusedComponent = chooser.getPreferredFocusedComponent();
520 final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
522 @NonNls final String s = toImplement ? "OverrideMethods" : "ImplementMethods";
523 final Shortcut[] shortcuts = keymap.getShortcuts(s);
525 if (shortcuts.length > 0 && shortcuts[0] instanceof KeyboardShortcut) {
526 preferredFocusedComponent.getInputMap().put(
527 ((KeyboardShortcut)shortcuts[0]).getFirstKeyStroke(), s
530 preferredFocusedComponent.getActionMap().put(
532 new AbstractAction() {
533 public void actionPerformed(final ActionEvent e) {
534 chooser.close(DialogWrapper.CANCEL_EXIT_CODE);
536 // invoke later in order to close previous modal dialog
537 ApplicationManager.getApplication().invokeLater(new Runnable() {
538 public void run() {
539 final CodeInsightActionHandler handler = toImplement ? new OverrideMethodsHandler(): new ImplementMethodsHandler();
540 handler.invoke(project, editor, aClass.getContainingFile());
549 public static void overrideOrImplementMethodsInRightPlace(Editor editor,
550 PsiClass aClass,
551 Collection<PsiMethodMember> candidates,
552 boolean copyJavadoc,
553 boolean insertOverrideWherePossible) {
554 try{
555 int offset = editor.getCaretModel().getOffset();
556 if (aClass.getLBrace() == null) {
557 PsiClass psiClass = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createClass("X");
558 aClass.addRangeAfter(psiClass.getLBrace(), psiClass.getRBrace(), aClass.getLastChild());
561 int lbraceOffset = aClass.getLBrace().getTextOffset();
562 List<PsiGenerationInfo<PsiMethod>> resultMembers;
563 if (offset <= lbraceOffset || aClass.isEnum()) {
564 resultMembers = new ArrayList<PsiGenerationInfo<PsiMethod>>();
565 for (PsiMethodMember candidate : candidates) {
566 Collection<PsiMethod> prototypes = overrideOrImplementMethod(aClass, candidate.getElement(), candidate.getSubstitutor(),
567 copyJavadoc, insertOverrideWherePossible);
568 for (PsiMethod prototype : prototypes) {
569 PsiElement anchor = getDefaultAnchorToOverrideOrImplement(aClass, candidate.getElement(), candidate.getSubstitutor());
570 PsiElement result = GenerateMembersUtil.insert(aClass, prototype, anchor, true);
571 resultMembers.add(new PsiGenerationInfo<PsiMethod>((PsiMethod)result));
575 else{
576 List<PsiGenerationInfo<PsiMethod>> prototypes = overrideOrImplementMethods(aClass, candidates, copyJavadoc, insertOverrideWherePossible);
577 resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes);
580 if (!resultMembers.isEmpty()) {
581 GenerateMembersUtil.positionCaret(editor, resultMembers.get(0).getPsiMember(), true);
584 catch(IncorrectOperationException e){
585 LOG.error(e);
589 @Nullable
590 public static PsiElement getDefaultAnchorToOverrideOrImplement(PsiClass aClass, PsiMethod baseMethod, PsiSubstitutor substitutor){
591 PsiMethod prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(baseMethod, PsiMethod.class);
592 while(prevBaseMethod != null) {
593 String name = prevBaseMethod.isConstructor() ? aClass.getName() : prevBaseMethod.getName();
594 //Happens when aClass instanceof PsiAnonymousClass
595 if (name != null) {
596 MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, prevBaseMethod.getParameterList(), prevBaseMethod.getTypeParameterList(), substitutor);
597 PsiMethod prevMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
598 if (prevMethod != null){
599 return prevMethod.getNextSibling();
602 prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(prevBaseMethod, PsiMethod.class);
605 PsiMethod nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(baseMethod, PsiMethod.class);
606 while(nextBaseMethod != null) {
607 String name = nextBaseMethod.isConstructor() ? aClass.getName() : nextBaseMethod.getName();
608 if (name != null) {
609 MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, nextBaseMethod.getParameterList(), nextBaseMethod.getTypeParameterList(), substitutor);
610 PsiMethod nextMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
611 if (nextMethod != null){
612 return nextMethod;
615 nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(nextBaseMethod, PsiMethod.class);
618 return null;
621 public static void overrideOrImplement(PsiClass psiClass, @NotNull PsiMethod baseMethod) throws IncorrectOperationException {
622 FileEditorManager fileEditorManager = FileEditorManager.getInstance(baseMethod.getProject());
624 List<PsiGenerationInfo<PsiMethod>> prototypes = convert2GenerationInfos(overrideOrImplementMethod(psiClass, baseMethod, false));
625 if (prototypes.isEmpty()) return;
627 PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseMethod.getContainingClass(), psiClass, PsiSubstitutor.EMPTY);
628 PsiElement anchor = getDefaultAnchorToOverrideOrImplement(psiClass, baseMethod, substitutor);
629 List<PsiGenerationInfo<PsiMethod>> results = GenerateMembersUtil.insertMembersBeforeAnchor(psiClass, anchor, prototypes);
631 PsiFile psiFile = psiClass.getContainingFile();
632 Editor editor = fileEditorManager.openTextEditor(new OpenFileDescriptor(psiFile.getProject(), psiFile.getVirtualFile()), false);
633 if (editor == null) return;
635 GenerateMembersUtil.positionCaret(editor, results.get(0).getPsiMember(), true);
636 editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
639 public static PsiClass getContextClass(Project project, Editor editor, PsiFile file, boolean allowInterface) {
640 PsiDocumentManager.getInstance(project).commitAllDocuments();
642 int offset = editor.getCaretModel().getOffset();
643 PsiElement element = file.findElementAt(offset);
644 do {
645 element = PsiTreeUtil.getParentOfType(element, PsiClass.class);
647 while (element instanceof PsiTypeParameter);
649 final PsiClass aClass = (PsiClass)element;
650 if (aClass instanceof JspClass) return null;
651 return aClass == null ||
652 !allowInterface && aClass.isInterface() ? null : aClass;
655 private static PsiSubstitutor getContextSubstitutor(PsiClass aClass) {
656 if (aClass instanceof PsiAnonymousClass) {
657 return ((PsiAnonymousClass)aClass).getBaseClassType().resolveGenerics().getSubstitutor();
660 return PsiSubstitutor.EMPTY;
663 public static void overrideOrImplementMethodsInRightPlace(Editor editor1, PsiClass aClass, Collection<PsiMethodMember> members, boolean copyJavadoc) {
664 boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
665 overrideOrImplementMethodsInRightPlace(editor1, aClass, members, copyJavadoc, insert);
668 public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass, Collection<CandidateInfo> candidatesToImplement,
669 boolean copyJavadoc) throws IncorrectOperationException {
670 boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
671 return overrideOrImplementMethodCandidates(aClass, candidatesToImplement, copyJavadoc, insert);