always use getIcon instead of findIcon
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / generation / OverrideImplementUtil.java
blob4b7f710a464d023cf04bba3cb552bc8577382b3e
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.IconLoader;
33 import com.intellij.openapi.util.Ref;
34 import com.intellij.psi.*;
35 import com.intellij.psi.codeStyle.CodeStyleManager;
36 import com.intellij.psi.codeStyle.CodeStyleSettings;
37 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
38 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
39 import com.intellij.psi.impl.source.jsp.jspJava.JspClass;
40 import com.intellij.psi.infos.CandidateInfo;
41 import com.intellij.psi.javadoc.PsiDocComment;
42 import com.intellij.psi.util.*;
43 import com.intellij.util.ArrayUtil;
44 import com.intellij.util.Function;
45 import com.intellij.util.IncorrectOperationException;
46 import com.intellij.util.containers.ContainerUtil;
47 import com.intellij.util.containers.HashMap;
48 import org.jetbrains.annotations.NonNls;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
52 import javax.swing.*;
53 import java.awt.event.ActionEvent;
54 import java.awt.event.InputEvent;
55 import java.awt.event.KeyEvent;
56 import java.util.*;
58 public class OverrideImplementUtil {
59 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.OverrideImplementUtil");
61 @NonNls private static final String PROP_COMBINED_OVERRIDE_IMPLEMENT = "OverrideImplement.combined";
63 private OverrideImplementUtil() {
66 @NotNull
67 public static Collection<CandidateInfo> getMethodsToOverrideImplement(PsiClass aClass, boolean toImplement) {
68 return getMapToOverrideImplement(aClass, toImplement).values();
71 @NotNull
72 public static Collection<MethodSignature> getMethodSignaturesToImplement(@NotNull PsiClass aClass) {
73 return getMapToOverrideImplement(aClass, true).keySet();
76 @NotNull
77 public static Collection<MethodSignature> getMethodSignaturesToOverride(@NotNull PsiClass aClass) {
78 return getMapToOverrideImplement(aClass, false).keySet();
81 @NotNull
82 private static Map<MethodSignature, CandidateInfo> getMapToOverrideImplement(PsiClass aClass, boolean toImplement) {
83 final PsiSubstitutor contextSubstitutor = getContextSubstitutor(aClass);
84 Map<MethodSignature, PsiMethod> abstracts = new LinkedHashMap<MethodSignature,PsiMethod>();
85 Map<MethodSignature, PsiMethod> finals = new HashMap<MethodSignature,PsiMethod>();
86 Map<MethodSignature, PsiMethod> concretes = new LinkedHashMap<MethodSignature,PsiMethod>();
87 Map<PsiClass, PsiSubstitutor> substitutors = new HashMap<PsiClass,PsiSubstitutor>();
89 Collection<HierarchicalMethodSignature> allMethodSigs = aClass.getVisibleSignatures();
90 PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
91 for (HierarchicalMethodSignature methodSig : allMethodSigs) {
92 PsiMethod method = methodSig.getMethod();
93 if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) continue;
94 PsiClass hisClass = method.getContainingClass();
95 if (hisClass == null) continue;
96 //Filter non-immediate super constructors
97 if (method.isConstructor() && (!aClass.isInheritor(hisClass, false) || aClass instanceof PsiAnonymousClass || aClass.isEnum())) {
98 continue;
101 PsiSubstitutor substitutor;
102 if ((substitutor = substitutors.get(hisClass)) == null) {
103 substitutor = aClass.isInheritor(hisClass, true) ?
104 TypeConversionUtil.getSuperClassSubstitutor(hisClass, aClass, PsiSubstitutor.EMPTY) : PsiSubstitutor.EMPTY;
105 substitutor = substitutor.putAll(contextSubstitutor);
106 substitutors.put(hisClass, substitutor);
109 String name = method.isConstructor() ? aClass.getName() : method.getName();
110 substitutor = GenerateMembersUtil.correctSubstitutor(method, substitutor);
112 PsiTypeParameterList typeParameterList = PsiUtil.isRawSubstitutor(method, substitutor) ? null : method.getTypeParameterList();
114 MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, method.getParameterList(), typeParameterList,
115 substitutor);
116 if (MethodSignatureUtil.findMethodBySignature(aClass, signature, false) != null) continue;
118 if (method.hasModifierProperty(PsiModifier.FINAL)) {
119 finals.put(signature, method);
120 continue;
123 Map<MethodSignature, PsiMethod> map = hisClass.isInterface() || method.hasModifierProperty(PsiModifier.ABSTRACT) ? abstracts : concretes;
124 PsiMethod other = map.get(signature);
125 if (other == null || preferLeftForImplement(method, other)) {
126 map.put(signature, method);
130 Map<MethodSignature, CandidateInfo> result = new LinkedHashMap<MethodSignature,CandidateInfo>();
131 if (toImplement || aClass.isInterface()) {
132 for (Map.Entry<MethodSignature, PsiMethod> entry : abstracts.entrySet()) {
133 MethodSignature signature = entry.getKey();
134 PsiMethod abstractOne = entry.getValue();
135 PsiMethod concrete = concretes.get(signature);
136 if (concrete == null
137 || PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList())
138 || !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) {
139 if (finals.get(signature) == null) {
140 PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne,
141 substitutors.get(abstractOne.getContainingClass()));
142 CandidateInfo info = new CandidateInfo(abstractOne, subst);
143 result.put(signature, info);
148 for (final MethodImplementor implementor : getImplementors()) {
149 for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) {
150 MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(),
151 method.getTypeParameterList(), PsiSubstitutor.EMPTY);
152 CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY);
153 result.put(signature, info);
157 else {
158 for (Map.Entry<MethodSignature, PsiMethod> entry : concretes.entrySet()) {
159 MethodSignature signature = entry.getKey();
160 PsiMethod concrete = entry.getValue();
161 if (finals.get(signature) == null) {
162 PsiMethod abstractOne = abstracts.get(signature);
163 if (abstractOne == null || !abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true) ||
164 CommonClassNames.JAVA_LANG_OBJECT.equals(concrete.getContainingClass().getQualifiedName())) {
165 PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(concrete, substitutors.get(concrete.getContainingClass()));
166 CandidateInfo info = new CandidateInfo(concrete, subst);
167 result.put(signature, info);
173 return result;
176 private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) {
177 if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true;
178 if (!left.getContainingClass().isInterface()) return true;
179 if (!right.getContainingClass().isInterface()) return false;
180 // implement annotated method
181 PsiAnnotation[] leftAnnotations = left.getModifierList().getAnnotations();
182 PsiAnnotation[] rightAnnotations = right.getModifierList().getAnnotations();
183 return leftAnnotations.length > rightAnnotations.length;
186 private static MethodImplementor[] getImplementors() {
187 return Extensions.getExtensions(MethodImplementor.EXTENSION_POINT_NAME);
191 * generate methods (with bodies) corresponding to given method declaration
192 * there are maybe two method implementations for one declaration
193 * (e.g. EJB' create() -> ejbCreate(), ejbPostCreate() )
194 * @param aClass context for method implementations
195 * @param method method to override or implement
196 * @param toCopyJavaDoc true if copy JavaDoc from method declaration
197 * @return list of method prototypes
199 @NotNull
200 public static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass, PsiMethod method, boolean toCopyJavaDoc) throws IncorrectOperationException {
201 final PsiClass containingClass = method.getContainingClass();
202 PsiSubstitutor substitutor = aClass.isInheritor(containingClass, true) ?
203 TypeConversionUtil.getSuperClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY) : PsiSubstitutor.EMPTY;
204 return overrideOrImplementMethod(aClass, method, substitutor, toCopyJavaDoc, true);
207 private static boolean isInsertOverride(PsiMethod superMethod, PsiClass targetClass) {
208 if (!CodeStyleSettingsManager.getSettings(targetClass.getProject()).INSERT_OVERRIDE_ANNOTATION || !PsiUtil.isLanguageLevel5OrHigher(targetClass)) {
209 return false;
211 if (PsiUtil.isLanguageLevel6OrHigher(targetClass)) return true;
212 if (targetClass.isInterface()) return true;
213 PsiClass superClass = superMethod.getContainingClass();
214 return !superClass.isInterface();
217 @NotNull
218 private static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass,
219 PsiMethod method,
220 PsiSubstitutor substitutor,
221 boolean toCopyJavaDoc,
222 boolean insertAtOverrideIfPossible) throws IncorrectOperationException {
223 if (!method.isValid() || !substitutor.isValid()) return Collections.emptyList();
225 List<PsiMethod> results = new ArrayList<PsiMethod>();
226 for (final MethodImplementor implementor : getImplementors()) {
227 results.addAll(Arrays.asList(implementor.createImplementationPrototypes(aClass, method)));
229 if (results.isEmpty()) {
230 PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor);
232 PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
233 PsiMethod result = (PsiMethod)factory.createClass("Dummy").add(method1);
234 if (result instanceof PsiAnnotationMethod) {
235 PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)result).getDefaultValue();
236 if (defaultValue != null) {
237 PsiElement defaultKeyword = defaultValue;
238 while (!(defaultKeyword instanceof PsiKeyword) && defaultKeyword != null) {
239 defaultKeyword = defaultKeyword.getPrevSibling();
241 if (defaultKeyword == null) defaultKeyword = defaultValue;
242 defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue);
245 results.add(result);
248 for (Iterator<PsiMethod> iterator = results.iterator(); iterator.hasNext();) {
249 PsiMethod result = iterator.next();
250 PsiUtil.setModifierProperty(result, PsiModifier.ABSTRACT, aClass.isInterface());
251 PsiUtil.setModifierProperty(result, PsiModifier.NATIVE, false);
253 if (!toCopyJavaDoc){
254 PsiDocComment comment = result.getDocComment();
255 if (comment != null){
256 comment.delete();
260 if (insertAtOverrideIfPossible && isInsertOverride(method, aClass) && !method.isConstructor()) {
261 annotate(result, "java.lang.Override");
264 for (OverrideImplementsAnnotationsHandler annotationsHandler : Extensions
265 .getExtensions(OverrideImplementsAnnotationsHandler.EP_NAME)) {
266 for (String annotationFQName : annotationsHandler.getAnnotations()) {
267 if (AnnotationUtil.isAnnotated(method, annotationFQName, false)) {
268 annotate(result, annotationFQName, annotationsHandler.annotationsToRemove(annotationFQName));
273 final PsiCodeBlock body = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createCodeBlockFromText("{}", null);
274 PsiCodeBlock oldbody = result.getBody();
275 if (oldbody != null){
276 oldbody.replace(body);
278 else{
279 result.add(body);
282 setupMethodBody(result, method, aClass);
284 // probably, it's better to reformat the whole method - it can go from other style sources
285 final Project project = method.getProject();
286 CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
287 CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project);
288 boolean keepBreaks = settings.KEEP_LINE_BREAKS;
289 settings.KEEP_LINE_BREAKS = false;
290 result = (PsiMethod)JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);
291 result = (PsiMethod)codeStyleManager.reformat(result);
292 settings.KEEP_LINE_BREAKS = keepBreaks;
294 if (aClass.findMethodBySignature(result, false) != null) {
295 iterator.remove();
299 return results;
302 private static void annotate(final PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException {
303 Project project = result.getProject();
304 AddAnnotationFix fix = new AddAnnotationFix(fqn, result, annosToRemove);
305 if (fix.isAvailable(project, null, result.getContainingFile())) {
306 fix.invoke(project, null, result.getContainingFile());
310 public static boolean isOverridable(PsiMethod method) {
311 return !method.isConstructor()
312 && !method.hasModifierProperty(PsiModifier.STATIC)
313 && !method.hasModifierProperty(PsiModifier.FINAL)
314 && !method.hasModifierProperty(PsiModifier.PRIVATE);
317 @NotNull
318 public static List<PsiGenerationInfo<PsiMethod>> overrideOrImplementMethods(PsiClass aClass,
319 Collection<PsiMethodMember> candidates,
320 boolean toCopyJavaDoc,
321 boolean toInsertAtOverride)
322 throws IncorrectOperationException {
323 List<CandidateInfo> candidateInfos = ContainerUtil.map2List(candidates, new Function<PsiMethodMember, CandidateInfo>() {
324 public CandidateInfo fun(final PsiMethodMember s) {
325 return new CandidateInfo(s.getElement(), s.getSubstitutor());
328 final List<PsiMethod> methods = overrideOrImplementMethodCandidates(aClass, candidateInfos, toCopyJavaDoc, toInsertAtOverride);
329 return convert2GenerationInfos(methods);
332 @NotNull
333 public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass,
334 Collection<CandidateInfo> candidates,
335 boolean toCopyJavaDoc,
336 boolean insertOverrideWherePossible) throws IncorrectOperationException {
337 List<PsiMethod> result = new ArrayList<PsiMethod>();
338 for (CandidateInfo candidateInfo : candidates) {
339 result.addAll(overrideOrImplementMethod(aClass, (PsiMethod)candidateInfo.getElement(), candidateInfo.getSubstitutor(),
340 toCopyJavaDoc, insertOverrideWherePossible));
342 return result;
345 public static List<PsiGenerationInfo<PsiMethod>> convert2GenerationInfos(final Collection<PsiMethod> methods) {
346 return ContainerUtil.map2List(methods, new Function<PsiMethod, PsiGenerationInfo<PsiMethod>>() {
347 public PsiGenerationInfo<PsiMethod> fun(final PsiMethod s) {
348 return new PsiGenerationInfo<PsiMethod>(s);
353 @NotNull
354 private static String callSuper (PsiMethod superMethod, PsiMethod overriding) {
355 @NonNls StringBuilder buffer = new StringBuilder();
356 if (!superMethod.isConstructor() && superMethod.getReturnType() != PsiType.VOID) {
357 buffer.append("return ");
359 buffer.append("super");
360 PsiParameter[] parms = overriding.getParameterList().getParameters();
361 if (!superMethod.isConstructor()){
362 buffer.append(".");
363 buffer.append(superMethod.getName());
365 buffer.append("(");
366 for (int i = 0; i < parms.length; i++) {
367 String name = parms[i].getName();
368 if (i > 0) buffer.append(",");
369 buffer.append(name);
371 buffer.append(")");
372 return buffer.toString();
375 public static void setupMethodBody(PsiMethod result, PsiMethod originalMethod, PsiClass targetClass) throws IncorrectOperationException {
376 String templName = originalMethod.hasModifierProperty(PsiModifier.ABSTRACT) ?
377 JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY;
378 FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName);
379 setupMethodBody(result, originalMethod, targetClass, template);
382 public static void setupMethodBody(final PsiMethod result, final PsiMethod originalMethod, final PsiClass targetClass,
383 final FileTemplate template) throws IncorrectOperationException {
384 if (targetClass.isInterface()) {
385 final PsiCodeBlock body = result.getBody();
386 if (body != null) body.delete();
389 FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(template.getExtension());
390 PsiType returnType = result.getReturnType();
391 if (returnType == null) {
392 returnType = PsiType.VOID;
394 Properties properties = new Properties();
395 properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnType.getPresentableText());
396 properties.setProperty(FileTemplate.ATTRIBUTE_DEFAULT_RETURN_VALUE, PsiTypesUtil.getDefaultValueOfType(returnType));
397 properties.setProperty(FileTemplate.ATTRIBUTE_CALL_SUPER, callSuper(originalMethod, result));
398 JavaTemplateUtil.setClassAndMethodNameProperties(properties, targetClass, result);
400 PsiElementFactory factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory();
401 @NonNls String methodText;
402 try {
403 String bodyText = template.getText(properties);
404 if (!"".equals(bodyText)) bodyText += "\n";
405 methodText = "void foo () {\n" + bodyText + "}";
406 methodText = FileTemplateUtil.indent(methodText, result.getProject(), fileType);
407 } catch (Exception e) {
408 throw new IncorrectOperationException("Failed to parse file template",e);
410 if (methodText != null) {
411 PsiMethod m;
412 try {
413 m = factory.createMethodFromText(methodText, originalMethod);
415 catch (IncorrectOperationException e) {
416 ApplicationManager.getApplication().invokeLater(new Runnable() {
417 public void run() {
418 Messages.showErrorDialog(CodeInsightBundle.message("override.implement.broken.file.template.message"),
419 CodeInsightBundle.message("override.implement.broken.file.template.title"));
422 return;
424 PsiCodeBlock oldBody = result.getBody();
425 if (oldBody != null) {
426 oldBody.replace(m.getBody());
431 public static void chooseAndOverrideMethods(Project project, Editor editor, PsiClass aClass){
432 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.overrideimplement");
433 chooseAndOverrideOrImplementMethods(project, editor, aClass, false);
436 public static void chooseAndImplementMethods(Project project, Editor editor, PsiClass aClass){
437 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.overrideimplement");
438 chooseAndOverrideOrImplementMethods(project, editor, aClass, true);
441 private static void chooseAndOverrideOrImplementMethods(final Project project,
442 final Editor editor,
443 final PsiClass aClass,
444 final boolean toImplement){
445 LOG.assertTrue(aClass.isValid());
446 ApplicationManager.getApplication().assertReadAccessAllowed();
448 Collection<CandidateInfo> candidates = getMethodsToOverrideImplement(aClass, toImplement);
449 Collection<CandidateInfo> secondary = toImplement ? Collections.<CandidateInfo>emptyList() : getMethodsToOverrideImplement(aClass, true);
451 if (candidates.isEmpty() && secondary.isEmpty()) return;
453 final PsiMethodMember[] onlyPrimary = convertToMethodMembers(candidates);
454 final PsiMethodMember[] all = ArrayUtil.mergeArrays(onlyPrimary, convertToMethodMembers(secondary), PsiMethodMember.class);
456 final String toMerge = PropertiesComponent.getInstance(project).getValue(PROP_COMBINED_OVERRIDE_IMPLEMENT);
457 final Ref<Boolean> merge = Ref.create(!"false".equals(toMerge));
459 final boolean isAll = merge.get().booleanValue();
460 final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(isAll ? all : onlyPrimary, false, true, project,
461 PsiUtil.isLanguageLevel5OrHigher(aClass)) {
463 @Override
464 protected void fillToolbarActions(DefaultActionGroup group) {
465 super.fillToolbarActions(group);
466 if (toImplement) return;
468 final ToggleAction mergeAction = new ToggleAction("Show methods to implement", "Show methods to implement", IconLoader.getIcon("/general/show_to_implement.png")) {
469 @Override
470 public boolean isSelected(AnActionEvent e) {
471 return merge.get().booleanValue();
474 @Override
475 public void setSelected(AnActionEvent e, boolean state) {
476 merge.set(state);
477 resetElements(state ? all : onlyPrimary);
478 setTitle(getChooserTitle(false, merge));
481 mergeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.ALT_MASK)), myTree);
482 group.add(mergeAction);
485 chooser.setTitle(getChooserTitle(toImplement, merge));
486 registerHandlerForComplementaryAction(project, editor, aClass, toImplement, chooser);
488 chooser.setCopyJavadocVisible(true);
490 if (toImplement) {
491 chooser.selectElements(isAll ? all : onlyPrimary);
494 chooser.show();
495 if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return;
497 PropertiesComponent.getInstance(project).setValue(PROP_COMBINED_OVERRIDE_IMPLEMENT, merge.get().toString());
499 final List<PsiMethodMember> selectedElements = chooser.getSelectedElements();
500 if (selectedElements == null || selectedElements.isEmpty()) return;
502 new WriteCommandAction(project, aClass.getContainingFile()) {
503 protected void run(final Result result) throws Throwable {
504 overrideOrImplementMethodsInRightPlace(editor, aClass, selectedElements, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation());
506 }.execute();
509 private static String getChooserTitle(boolean toImplement, Ref<Boolean> merge) {
510 return toImplement
511 ? CodeInsightBundle.message("methods.to.implement.chooser.title")
512 : merge.get().booleanValue()
513 ? CodeInsightBundle.message("methods.to.override.implement.chooser.title")
514 : CodeInsightBundle.message("methods.to.override.chooser.title");
517 private static PsiMethodMember[] convertToMethodMembers(Collection<CandidateInfo> candidates) {
518 return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function<CandidateInfo, PsiMethodMember>() {
519 public PsiMethodMember fun(final CandidateInfo s) {
520 return new PsiMethodMember(s);
525 private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiClass aClass,
526 final boolean toImplement,
527 final MemberChooser<PsiMethodMember> chooser) {
528 final JComponent preferredFocusedComponent = chooser.getPreferredFocusedComponent();
529 final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
531 @NonNls final String s = toImplement ? "OverrideMethods" : "ImplementMethods";
532 final Shortcut[] shortcuts = keymap.getShortcuts(s);
534 if (shortcuts.length > 0 && shortcuts[0] instanceof KeyboardShortcut) {
535 preferredFocusedComponent.getInputMap().put(
536 ((KeyboardShortcut)shortcuts[0]).getFirstKeyStroke(), s
539 preferredFocusedComponent.getActionMap().put(
541 new AbstractAction() {
542 public void actionPerformed(final ActionEvent e) {
543 chooser.close(DialogWrapper.CANCEL_EXIT_CODE);
545 // invoke later in order to close previous modal dialog
546 ApplicationManager.getApplication().invokeLater(new Runnable() {
547 public void run() {
548 final CodeInsightActionHandler handler = toImplement ? new OverrideMethodsHandler(): new ImplementMethodsHandler();
549 handler.invoke(project, editor, aClass.getContainingFile());
558 public static void overrideOrImplementMethodsInRightPlace(Editor editor,
559 PsiClass aClass,
560 Collection<PsiMethodMember> candidates,
561 boolean copyJavadoc,
562 boolean insertOverrideWherePossible) {
563 try{
564 int offset = editor.getCaretModel().getOffset();
565 if (aClass.getLBrace() == null) {
566 PsiClass psiClass = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createClass("X");
567 aClass.addRangeAfter(psiClass.getLBrace(), psiClass.getRBrace(), aClass.getLastChild());
570 int lbraceOffset = aClass.getLBrace().getTextOffset();
571 List<PsiGenerationInfo<PsiMethod>> resultMembers;
572 if (offset <= lbraceOffset || aClass.isEnum()) {
573 resultMembers = new ArrayList<PsiGenerationInfo<PsiMethod>>();
574 for (PsiMethodMember candidate : candidates) {
575 Collection<PsiMethod> prototypes = overrideOrImplementMethod(aClass, candidate.getElement(), candidate.getSubstitutor(),
576 copyJavadoc, insertOverrideWherePossible);
577 for (PsiMethod prototype : prototypes) {
578 PsiElement anchor = getDefaultAnchorToOverrideOrImplement(aClass, candidate.getElement(), candidate.getSubstitutor());
579 PsiElement result = GenerateMembersUtil.insert(aClass, prototype, anchor, true);
580 resultMembers.add(new PsiGenerationInfo<PsiMethod>((PsiMethod)result));
584 else{
585 List<PsiGenerationInfo<PsiMethod>> prototypes = overrideOrImplementMethods(aClass, candidates, copyJavadoc, insertOverrideWherePossible);
586 resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes);
589 if (!resultMembers.isEmpty()) {
590 GenerateMembersUtil.positionCaret(editor, resultMembers.get(0).getPsiMember(), true);
593 catch(IncorrectOperationException e){
594 LOG.error(e);
598 @Nullable
599 public static PsiElement getDefaultAnchorToOverrideOrImplement(PsiClass aClass, PsiMethod baseMethod, PsiSubstitutor substitutor){
600 PsiMethod prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(baseMethod, PsiMethod.class);
601 while(prevBaseMethod != null) {
602 String name = prevBaseMethod.isConstructor() ? aClass.getName() : prevBaseMethod.getName();
603 //Happens when aClass instanceof PsiAnonymousClass
604 if (name != null) {
605 MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, prevBaseMethod.getParameterList(), prevBaseMethod.getTypeParameterList(), substitutor);
606 PsiMethod prevMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
607 if (prevMethod != null){
608 return prevMethod.getNextSibling();
611 prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(prevBaseMethod, PsiMethod.class);
614 PsiMethod nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(baseMethod, PsiMethod.class);
615 while(nextBaseMethod != null) {
616 String name = nextBaseMethod.isConstructor() ? aClass.getName() : nextBaseMethod.getName();
617 if (name != null) {
618 MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, nextBaseMethod.getParameterList(), nextBaseMethod.getTypeParameterList(), substitutor);
619 PsiMethod nextMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
620 if (nextMethod != null){
621 return nextMethod;
624 nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(nextBaseMethod, PsiMethod.class);
627 return null;
630 public static void overrideOrImplement(PsiClass psiClass, @NotNull PsiMethod baseMethod) throws IncorrectOperationException {
631 FileEditorManager fileEditorManager = FileEditorManager.getInstance(baseMethod.getProject());
633 List<PsiGenerationInfo<PsiMethod>> prototypes = convert2GenerationInfos(overrideOrImplementMethod(psiClass, baseMethod, false));
634 if (prototypes.isEmpty()) return;
636 PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseMethod.getContainingClass(), psiClass, PsiSubstitutor.EMPTY);
637 PsiElement anchor = getDefaultAnchorToOverrideOrImplement(psiClass, baseMethod, substitutor);
638 List<PsiGenerationInfo<PsiMethod>> results = GenerateMembersUtil.insertMembersBeforeAnchor(psiClass, anchor, prototypes);
640 PsiFile psiFile = psiClass.getContainingFile();
641 Editor editor = fileEditorManager.openTextEditor(new OpenFileDescriptor(psiFile.getProject(), psiFile.getVirtualFile()), false);
642 if (editor == null) return;
644 GenerateMembersUtil.positionCaret(editor, results.get(0).getPsiMember(), true);
645 editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
648 public static PsiClass getContextClass(Project project, Editor editor, PsiFile file, boolean allowInterface) {
649 PsiDocumentManager.getInstance(project).commitAllDocuments();
651 int offset = editor.getCaretModel().getOffset();
652 PsiElement element = file.findElementAt(offset);
653 do {
654 element = PsiTreeUtil.getParentOfType(element, PsiClass.class);
656 while (element instanceof PsiTypeParameter);
658 final PsiClass aClass = (PsiClass)element;
659 if (aClass instanceof JspClass) return null;
660 return aClass == null ||
661 !allowInterface && aClass.isInterface() ? null : aClass;
664 private static PsiSubstitutor getContextSubstitutor(PsiClass aClass) {
665 if (aClass instanceof PsiAnonymousClass) {
666 return ((PsiAnonymousClass)aClass).getBaseClassType().resolveGenerics().getSubstitutor();
669 return PsiSubstitutor.EMPTY;
672 public static void overrideOrImplementMethodsInRightPlace(Editor editor1, PsiClass aClass, Collection<PsiMethodMember> members, boolean copyJavadoc) {
673 boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
674 overrideOrImplementMethodsInRightPlace(editor1, aClass, members, copyJavadoc, insert);
677 public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass, Collection<CandidateInfo> candidatesToImplement,
678 boolean copyJavadoc) throws IncorrectOperationException {
679 boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
680 return overrideOrImplementMethodCandidates(aClass, candidatesToImplement, copyJavadoc, insert);