update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / generation / OverrideImplementUtil.java
blob9e27436e977a7e3811ba8a0dd875bbec3beb6ecc
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.codeInsight.generation;
18 import com.intellij.codeInsight.AnnotationUtil;
19 import com.intellij.codeInsight.CodeInsightActionHandler;
20 import com.intellij.codeInsight.CodeInsightBundle;
21 import com.intellij.codeInsight.MethodImplementor;
22 import com.intellij.codeInsight.intention.AddAnnotationFix;
23 import com.intellij.featureStatistics.FeatureUsageTracker;
24 import com.intellij.ide.fileTemplates.FileTemplate;
25 import com.intellij.ide.fileTemplates.FileTemplateManager;
26 import com.intellij.ide.fileTemplates.FileTemplateUtil;
27 import com.intellij.ide.fileTemplates.JavaTemplateUtil;
28 import com.intellij.ide.util.MemberChooser;
29 import com.intellij.ide.util.PropertiesComponent;
30 import com.intellij.openapi.actionSystem.*;
31 import com.intellij.openapi.application.ApplicationManager;
32 import com.intellij.openapi.application.Result;
33 import com.intellij.openapi.command.WriteCommandAction;
34 import com.intellij.openapi.diagnostic.Logger;
35 import com.intellij.openapi.editor.Editor;
36 import com.intellij.openapi.editor.ScrollType;
37 import com.intellij.openapi.extensions.Extensions;
38 import com.intellij.openapi.fileEditor.FileEditorManager;
39 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
40 import com.intellij.openapi.fileTypes.FileType;
41 import com.intellij.openapi.fileTypes.FileTypeManager;
42 import com.intellij.openapi.keymap.Keymap;
43 import com.intellij.openapi.keymap.KeymapManager;
44 import com.intellij.openapi.project.Project;
45 import com.intellij.openapi.ui.DialogWrapper;
46 import com.intellij.openapi.ui.Messages;
47 import com.intellij.openapi.util.IconLoader;
48 import com.intellij.openapi.util.Ref;
49 import com.intellij.psi.*;
50 import com.intellij.psi.codeStyle.CodeStyleManager;
51 import com.intellij.psi.codeStyle.CodeStyleSettings;
52 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
53 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
54 import com.intellij.psi.impl.source.jsp.jspJava.JspClass;
55 import com.intellij.psi.infos.CandidateInfo;
56 import com.intellij.psi.javadoc.PsiDocComment;
57 import com.intellij.psi.util.*;
58 import com.intellij.util.ArrayUtil;
59 import com.intellij.util.Function;
60 import com.intellij.util.IncorrectOperationException;
61 import com.intellij.util.containers.ContainerUtil;
62 import org.jetbrains.annotations.NonNls;
63 import org.jetbrains.annotations.NotNull;
64 import org.jetbrains.annotations.Nullable;
66 import javax.swing.*;
67 import java.awt.event.ActionEvent;
68 import java.awt.event.InputEvent;
69 import java.awt.event.KeyEvent;
70 import java.util.*;
72 public class OverrideImplementUtil {
73 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.OverrideImplementUtil");
75 @NonNls private static final String PROP_COMBINED_OVERRIDE_IMPLEMENT = "OverrideImplement.combined";
77 private OverrideImplementUtil() {
80 @NotNull
81 public static Collection<CandidateInfo> getMethodsToOverrideImplement(PsiClass aClass, boolean toImplement) {
82 return getMapToOverrideImplement(aClass, toImplement).values();
85 @NotNull
86 public static Collection<MethodSignature> getMethodSignaturesToImplement(@NotNull PsiClass aClass) {
87 return getMapToOverrideImplement(aClass, true).keySet();
90 @NotNull
91 public static Collection<MethodSignature> getMethodSignaturesToOverride(@NotNull PsiClass aClass) {
92 return getMapToOverrideImplement(aClass, false).keySet();
95 @NotNull
96 private static Map<MethodSignature, CandidateInfo> getMapToOverrideImplement(PsiClass aClass, boolean toImplement) {
97 Map<MethodSignature, PsiMethod> abstracts = new LinkedHashMap<MethodSignature,PsiMethod>();
98 Map<MethodSignature, PsiMethod> finals = new LinkedHashMap<MethodSignature,PsiMethod>();
99 Map<MethodSignature, PsiMethod> concretes = new LinkedHashMap<MethodSignature,PsiMethod>();
101 Collection<HierarchicalMethodSignature> allMethodSigs = aClass.getVisibleSignatures();
102 PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
103 for (HierarchicalMethodSignature signature : allMethodSigs) {
104 PsiMethod method = signature.getMethod();
105 if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) continue;
106 PsiClass hisClass = method.getContainingClass();
107 if (hisClass == null) continue;
108 //Filter non-immediate super constructors
109 if (method.isConstructor() && (!aClass.isInheritor(hisClass, false) || aClass instanceof PsiAnonymousClass || aClass.isEnum())) {
110 continue;
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 TreeMap<MethodSignature,CandidateInfo>(new Comparator<MethodSignature>() {
128 // signatures should appear in the order of declaration
129 public int compare(MethodSignature o1, MethodSignature o2) {
130 int offset1 = o1 instanceof MethodSignatureBackedByPsiMethod ? ((MethodSignatureBackedByPsiMethod)o1).getMethod().getTextOffset() : -1;
131 int offset2 = o2 instanceof MethodSignatureBackedByPsiMethod ? ((MethodSignatureBackedByPsiMethod)o2).getMethod().getTextOffset() : -2;
132 return offset1 - offset2;
135 if (toImplement || aClass.isInterface()) {
136 for (Map.Entry<MethodSignature, PsiMethod> entry : abstracts.entrySet()) {
137 MethodSignature signature = entry.getKey();
138 PsiMethod abstractOne = entry.getValue();
139 PsiMethod concrete = concretes.get(signature);
140 if (concrete == null
141 || PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList())
142 || !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) {
143 if (finals.get(signature) == null) {
144 PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne, signature.getSubstitutor());
145 CandidateInfo info = new CandidateInfo(abstractOne, subst);
146 result.put(signature, info);
151 for (final MethodImplementor implementor : getImplementors()) {
152 for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) {
153 MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(),
154 method.getTypeParameterList(), PsiSubstitutor.EMPTY);
155 CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY);
156 result.put(signature, info);
160 else {
161 for (Map.Entry<MethodSignature, PsiMethod> entry : concretes.entrySet()) {
162 MethodSignature signature = entry.getKey();
163 PsiMethod concrete = entry.getValue();
164 if (finals.get(signature) == null) {
165 PsiMethod abstractOne = abstracts.get(signature);
166 if (abstractOne == null || !abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true) ||
167 CommonClassNames.JAVA_LANG_OBJECT.equals(concrete.getContainingClass().getQualifiedName())) {
168 PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(concrete, signature.getSubstitutor());
169 CandidateInfo info = new CandidateInfo(concrete, subst);
170 result.put(signature, info);
176 return result;
179 private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) {
180 if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true;
181 if (!left.getContainingClass().isInterface()) return true;
182 if (!right.getContainingClass().isInterface()) return false;
183 // implement annotated method
184 PsiAnnotation[] leftAnnotations = left.getModifierList().getAnnotations();
185 PsiAnnotation[] rightAnnotations = right.getModifierList().getAnnotations();
186 return leftAnnotations.length > rightAnnotations.length;
189 private static MethodImplementor[] getImplementors() {
190 return Extensions.getExtensions(MethodImplementor.EXTENSION_POINT_NAME);
194 * generate methods (with bodies) corresponding to given method declaration
195 * there are maybe two method implementations for one declaration
196 * (e.g. EJB' create() -> ejbCreate(), ejbPostCreate() )
197 * @param aClass context for method implementations
198 * @param method method to override or implement
199 * @param toCopyJavaDoc true if copy JavaDoc from method declaration
200 * @return list of method prototypes
202 @NotNull
203 public static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass, PsiMethod method, boolean toCopyJavaDoc) throws IncorrectOperationException {
204 final PsiClass containingClass = method.getContainingClass();
205 PsiSubstitutor substitutor = aClass.isInheritor(containingClass, true) ?
206 TypeConversionUtil.getSuperClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY) : PsiSubstitutor.EMPTY;
207 return overrideOrImplementMethod(aClass, method, substitutor, toCopyJavaDoc, true);
210 private static boolean isInsertOverride(PsiMethod superMethod, PsiClass targetClass) {
211 if (!CodeStyleSettingsManager.getSettings(targetClass.getProject()).INSERT_OVERRIDE_ANNOTATION || !PsiUtil.isLanguageLevel5OrHigher(targetClass)) {
212 return false;
214 if (PsiUtil.isLanguageLevel6OrHigher(targetClass)) return true;
215 if (targetClass.isInterface()) return true;
216 PsiClass superClass = superMethod.getContainingClass();
217 return !superClass.isInterface();
220 @NotNull
221 private static Collection<PsiMethod> overrideOrImplementMethod(PsiClass aClass,
222 PsiMethod method,
223 PsiSubstitutor substitutor,
224 boolean toCopyJavaDoc,
225 boolean insertAtOverrideIfPossible) throws IncorrectOperationException {
226 if (!method.isValid() || !substitutor.isValid()) return Collections.emptyList();
228 List<PsiMethod> results = new ArrayList<PsiMethod>();
229 for (final MethodImplementor implementor : getImplementors()) {
230 results.addAll(Arrays.asList(implementor.createImplementationPrototypes(aClass, method)));
232 if (results.isEmpty()) {
233 PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor);
235 PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
236 PsiMethod result = (PsiMethod)factory.createClass("Dummy").add(method1);
237 if (result instanceof PsiAnnotationMethod) {
238 PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)result).getDefaultValue();
239 if (defaultValue != null) {
240 PsiElement defaultKeyword = defaultValue;
241 while (!(defaultKeyword instanceof PsiKeyword) && defaultKeyword != null) {
242 defaultKeyword = defaultKeyword.getPrevSibling();
244 if (defaultKeyword == null) defaultKeyword = defaultValue;
245 defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue);
248 results.add(result);
251 for (Iterator<PsiMethod> iterator = results.iterator(); iterator.hasNext();) {
252 PsiMethod result = iterator.next();
253 PsiUtil.setModifierProperty(result, PsiModifier.ABSTRACT, aClass.isInterface());
254 PsiUtil.setModifierProperty(result, PsiModifier.NATIVE, false);
256 if (!toCopyJavaDoc){
257 PsiDocComment comment = result.getDocComment();
258 if (comment != null){
259 comment.delete();
263 if (insertAtOverrideIfPossible && isInsertOverride(method, aClass) && !method.isConstructor()) {
264 annotate(result, "java.lang.Override");
267 for (OverrideImplementsAnnotationsHandler annotationsHandler : Extensions
268 .getExtensions(OverrideImplementsAnnotationsHandler.EP_NAME)) {
269 for (String annotationFQName : annotationsHandler.getAnnotations()) {
270 if (AnnotationUtil.isAnnotated(method, annotationFQName, false)) {
271 annotate(result, annotationFQName, annotationsHandler.annotationsToRemove(annotationFQName));
276 final PsiCodeBlock body = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createCodeBlockFromText("{}", null);
277 PsiCodeBlock oldbody = result.getBody();
278 if (oldbody != null){
279 oldbody.replace(body);
281 else{
282 result.add(body);
285 setupMethodBody(result, method, aClass);
287 // probably, it's better to reformat the whole method - it can go from other style sources
288 final Project project = method.getProject();
289 CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
290 CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project);
291 boolean keepBreaks = settings.KEEP_LINE_BREAKS;
292 settings.KEEP_LINE_BREAKS = false;
293 result = (PsiMethod)JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);
294 result = (PsiMethod)codeStyleManager.reformat(result);
295 settings.KEEP_LINE_BREAKS = keepBreaks;
297 if (aClass.findMethodBySignature(result, false) != null) {
298 iterator.remove();
302 return results;
305 private static void annotate(final PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException {
306 Project project = result.getProject();
307 AddAnnotationFix fix = new AddAnnotationFix(fqn, result, annosToRemove);
308 if (fix.isAvailable(project, null, result.getContainingFile())) {
309 fix.invoke(project, null, result.getContainingFile());
313 public static boolean isOverridable(PsiMethod method) {
314 return !method.isConstructor()
315 && !method.hasModifierProperty(PsiModifier.STATIC)
316 && !method.hasModifierProperty(PsiModifier.FINAL)
317 && !method.hasModifierProperty(PsiModifier.PRIVATE);
320 @NotNull
321 public static List<PsiGenerationInfo<PsiMethod>> overrideOrImplementMethods(PsiClass aClass,
322 Collection<PsiMethodMember> candidates,
323 boolean toCopyJavaDoc,
324 boolean toInsertAtOverride)
325 throws IncorrectOperationException {
326 List<CandidateInfo> candidateInfos = ContainerUtil.map2List(candidates, new Function<PsiMethodMember, CandidateInfo>() {
327 public CandidateInfo fun(final PsiMethodMember s) {
328 return new CandidateInfo(s.getElement(), s.getSubstitutor());
331 final List<PsiMethod> methods = overrideOrImplementMethodCandidates(aClass, candidateInfos, toCopyJavaDoc, toInsertAtOverride);
332 return convert2GenerationInfos(methods);
335 @NotNull
336 public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass,
337 Collection<CandidateInfo> candidates,
338 boolean toCopyJavaDoc,
339 boolean insertOverrideWherePossible) throws IncorrectOperationException {
340 List<PsiMethod> result = new ArrayList<PsiMethod>();
341 for (CandidateInfo candidateInfo : candidates) {
342 result.addAll(overrideOrImplementMethod(aClass, (PsiMethod)candidateInfo.getElement(), candidateInfo.getSubstitutor(),
343 toCopyJavaDoc, insertOverrideWherePossible));
345 return result;
348 public static List<PsiGenerationInfo<PsiMethod>> convert2GenerationInfos(final Collection<PsiMethod> methods) {
349 return ContainerUtil.map2List(methods, new Function<PsiMethod, PsiGenerationInfo<PsiMethod>>() {
350 public PsiGenerationInfo<PsiMethod> fun(final PsiMethod s) {
351 return new PsiGenerationInfo<PsiMethod>(s);
356 @NotNull
357 private static String callSuper (PsiMethod superMethod, PsiMethod overriding) {
358 @NonNls StringBuilder buffer = new StringBuilder();
359 if (!superMethod.isConstructor() && superMethod.getReturnType() != PsiType.VOID) {
360 buffer.append("return ");
362 buffer.append("super");
363 PsiParameter[] parms = overriding.getParameterList().getParameters();
364 if (!superMethod.isConstructor()){
365 buffer.append(".");
366 buffer.append(superMethod.getName());
368 buffer.append("(");
369 for (int i = 0; i < parms.length; i++) {
370 String name = parms[i].getName();
371 if (i > 0) buffer.append(",");
372 buffer.append(name);
374 buffer.append(")");
375 return buffer.toString();
378 public static void setupMethodBody(PsiMethod result, PsiMethod originalMethod, PsiClass targetClass) throws IncorrectOperationException {
379 String templName = originalMethod.hasModifierProperty(PsiModifier.ABSTRACT) ?
380 JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY;
381 FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName);
382 setupMethodBody(result, originalMethod, targetClass, template);
385 public static void setupMethodBody(final PsiMethod result, final PsiMethod originalMethod, final PsiClass targetClass,
386 final FileTemplate template) throws IncorrectOperationException {
387 if (targetClass.isInterface()) {
388 final PsiCodeBlock body = result.getBody();
389 if (body != null) body.delete();
392 FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(template.getExtension());
393 PsiType returnType = result.getReturnType();
394 if (returnType == null) {
395 returnType = PsiType.VOID;
397 Properties properties = new Properties();
398 properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnType.getPresentableText());
399 properties.setProperty(FileTemplate.ATTRIBUTE_DEFAULT_RETURN_VALUE, PsiTypesUtil.getDefaultValueOfType(returnType));
400 properties.setProperty(FileTemplate.ATTRIBUTE_CALL_SUPER, callSuper(originalMethod, result));
401 JavaTemplateUtil.setClassAndMethodNameProperties(properties, targetClass, result);
403 PsiElementFactory factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory();
404 @NonNls String methodText;
405 try {
406 String bodyText = template.getText(properties);
407 if (!"".equals(bodyText)) bodyText += "\n";
408 methodText = "void foo () {\n" + bodyText + "}";
409 methodText = FileTemplateUtil.indent(methodText, result.getProject(), fileType);
410 } catch (Exception e) {
411 throw new IncorrectOperationException("Failed to parse file template",e);
413 if (methodText != null) {
414 PsiMethod m;
415 try {
416 m = factory.createMethodFromText(methodText, originalMethod);
418 catch (IncorrectOperationException e) {
419 ApplicationManager.getApplication().invokeLater(new Runnable() {
420 public void run() {
421 Messages.showErrorDialog(CodeInsightBundle.message("override.implement.broken.file.template.message"),
422 CodeInsightBundle.message("override.implement.broken.file.template.title"));
425 return;
427 PsiCodeBlock oldBody = result.getBody();
428 if (oldBody != null) {
429 oldBody.replace(m.getBody());
434 public static void chooseAndOverrideMethods(Project project, Editor editor, PsiClass aClass){
435 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.overrideimplement");
436 chooseAndOverrideOrImplementMethods(project, editor, aClass, false);
439 public static void chooseAndImplementMethods(Project project, Editor editor, PsiClass aClass){
440 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.overrideimplement");
441 chooseAndOverrideOrImplementMethods(project, editor, aClass, true);
444 private static void chooseAndOverrideOrImplementMethods(final Project project,
445 final Editor editor,
446 final PsiClass aClass,
447 final boolean toImplement){
448 LOG.assertTrue(aClass.isValid());
449 ApplicationManager.getApplication().assertReadAccessAllowed();
451 Collection<CandidateInfo> candidates = getMethodsToOverrideImplement(aClass, toImplement);
452 Collection<CandidateInfo> secondary = toImplement ? Collections.<CandidateInfo>emptyList() : getMethodsToOverrideImplement(aClass, true);
454 if (candidates.isEmpty() && secondary.isEmpty()) return;
456 final PsiMethodMember[] onlyPrimary = convertToMethodMembers(candidates);
457 final PsiMethodMember[] all = ArrayUtil.mergeArrays(onlyPrimary, convertToMethodMembers(secondary), PsiMethodMember.class);
459 final String toMerge = PropertiesComponent.getInstance(project).getValue(PROP_COMBINED_OVERRIDE_IMPLEMENT);
460 final Ref<Boolean> merge = Ref.create(!"false".equals(toMerge));
462 final boolean isAll = merge.get().booleanValue();
463 final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(isAll ? all : onlyPrimary, false, true, project,
464 PsiUtil.isLanguageLevel5OrHigher(aClass)) {
466 @Override
467 protected void fillToolbarActions(DefaultActionGroup group) {
468 super.fillToolbarActions(group);
469 if (toImplement) return;
471 final ToggleAction mergeAction = new ToggleAction("Show methods to implement", "Show methods to implement", IconLoader.getIcon("/general/show_to_implement.png")) {
472 @Override
473 public boolean isSelected(AnActionEvent e) {
474 return merge.get().booleanValue();
477 @Override
478 public void setSelected(AnActionEvent e, boolean state) {
479 merge.set(state);
480 resetElements(state ? all : onlyPrimary);
481 setTitle(getChooserTitle(false, merge));
484 mergeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.ALT_MASK)), myTree);
485 group.add(mergeAction);
488 chooser.setTitle(getChooserTitle(toImplement, merge));
489 registerHandlerForComplementaryAction(project, editor, aClass, toImplement, chooser);
491 chooser.setCopyJavadocVisible(true);
493 if (toImplement) {
494 chooser.selectElements(isAll ? all : onlyPrimary);
497 chooser.show();
498 if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return;
500 PropertiesComponent.getInstance(project).setValue(PROP_COMBINED_OVERRIDE_IMPLEMENT, merge.get().toString());
502 final List<PsiMethodMember> selectedElements = chooser.getSelectedElements();
503 if (selectedElements == null || selectedElements.isEmpty()) return;
505 new WriteCommandAction(project, aClass.getContainingFile()) {
506 protected void run(final Result result) throws Throwable {
507 overrideOrImplementMethodsInRightPlace(editor, aClass, selectedElements, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation());
509 }.execute();
512 private static String getChooserTitle(boolean toImplement, Ref<Boolean> merge) {
513 return toImplement
514 ? CodeInsightBundle.message("methods.to.implement.chooser.title")
515 : merge.get().booleanValue()
516 ? CodeInsightBundle.message("methods.to.override.implement.chooser.title")
517 : CodeInsightBundle.message("methods.to.override.chooser.title");
520 private static PsiMethodMember[] convertToMethodMembers(Collection<CandidateInfo> candidates) {
521 return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function<CandidateInfo, PsiMethodMember>() {
522 public PsiMethodMember fun(final CandidateInfo s) {
523 return new PsiMethodMember(s);
528 private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiClass aClass,
529 final boolean toImplement,
530 final MemberChooser<PsiMethodMember> chooser) {
531 final JComponent preferredFocusedComponent = chooser.getPreferredFocusedComponent();
532 final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
534 @NonNls final String s = toImplement ? "OverrideMethods" : "ImplementMethods";
535 final Shortcut[] shortcuts = keymap.getShortcuts(s);
537 if (shortcuts.length > 0 && shortcuts[0] instanceof KeyboardShortcut) {
538 preferredFocusedComponent.getInputMap().put(
539 ((KeyboardShortcut)shortcuts[0]).getFirstKeyStroke(), s
542 preferredFocusedComponent.getActionMap().put(
544 new AbstractAction() {
545 public void actionPerformed(final ActionEvent e) {
546 chooser.close(DialogWrapper.CANCEL_EXIT_CODE);
548 // invoke later in order to close previous modal dialog
549 ApplicationManager.getApplication().invokeLater(new Runnable() {
550 public void run() {
551 final CodeInsightActionHandler handler = toImplement ? new OverrideMethodsHandler(): new ImplementMethodsHandler();
552 handler.invoke(project, editor, aClass.getContainingFile());
561 public static void overrideOrImplementMethodsInRightPlace(Editor editor,
562 PsiClass aClass,
563 Collection<PsiMethodMember> candidates,
564 boolean copyJavadoc,
565 boolean insertOverrideWherePossible) {
566 try{
567 int offset = editor.getCaretModel().getOffset();
568 if (aClass.getLBrace() == null) {
569 PsiClass psiClass = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createClass("X");
570 aClass.addRangeAfter(psiClass.getLBrace(), psiClass.getRBrace(), aClass.getLastChild());
573 int lbraceOffset = aClass.getLBrace().getTextOffset();
574 List<PsiGenerationInfo<PsiMethod>> resultMembers;
575 if (offset <= lbraceOffset || aClass.isEnum()) {
576 resultMembers = new ArrayList<PsiGenerationInfo<PsiMethod>>();
577 for (PsiMethodMember candidate : candidates) {
578 Collection<PsiMethod> prototypes = overrideOrImplementMethod(aClass, candidate.getElement(), candidate.getSubstitutor(),
579 copyJavadoc, insertOverrideWherePossible);
580 for (PsiMethod prototype : prototypes) {
581 PsiElement anchor = getDefaultAnchorToOverrideOrImplement(aClass, candidate.getElement(), candidate.getSubstitutor());
582 PsiElement result = GenerateMembersUtil.insert(aClass, prototype, anchor, true);
583 resultMembers.add(new PsiGenerationInfo<PsiMethod>((PsiMethod)result));
587 else{
588 List<PsiGenerationInfo<PsiMethod>> prototypes = overrideOrImplementMethods(aClass, candidates, copyJavadoc, insertOverrideWherePossible);
589 resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes);
592 if (!resultMembers.isEmpty()) {
593 GenerateMembersUtil.positionCaret(editor, resultMembers.get(0).getPsiMember(), true);
596 catch(IncorrectOperationException e){
597 LOG.error(e);
601 @Nullable
602 public static PsiElement getDefaultAnchorToOverrideOrImplement(PsiClass aClass, PsiMethod baseMethod, PsiSubstitutor substitutor){
603 PsiMethod prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(baseMethod, PsiMethod.class);
604 while(prevBaseMethod != null) {
605 String name = prevBaseMethod.isConstructor() ? aClass.getName() : prevBaseMethod.getName();
606 //Happens when aClass instanceof PsiAnonymousClass
607 if (name != null) {
608 MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, prevBaseMethod.getParameterList(), prevBaseMethod.getTypeParameterList(), substitutor);
609 PsiMethod prevMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
610 if (prevMethod != null){
611 return prevMethod.getNextSibling();
614 prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(prevBaseMethod, PsiMethod.class);
617 PsiMethod nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(baseMethod, PsiMethod.class);
618 while(nextBaseMethod != null) {
619 String name = nextBaseMethod.isConstructor() ? aClass.getName() : nextBaseMethod.getName();
620 if (name != null) {
621 MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, nextBaseMethod.getParameterList(), nextBaseMethod.getTypeParameterList(), substitutor);
622 PsiMethod nextMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
623 if (nextMethod != null){
624 return nextMethod;
627 nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(nextBaseMethod, PsiMethod.class);
630 return null;
633 public static void overrideOrImplement(PsiClass psiClass, @NotNull PsiMethod baseMethod) throws IncorrectOperationException {
634 FileEditorManager fileEditorManager = FileEditorManager.getInstance(baseMethod.getProject());
636 List<PsiGenerationInfo<PsiMethod>> prototypes = convert2GenerationInfos(overrideOrImplementMethod(psiClass, baseMethod, false));
637 if (prototypes.isEmpty()) return;
639 PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseMethod.getContainingClass(), psiClass, PsiSubstitutor.EMPTY);
640 PsiElement anchor = getDefaultAnchorToOverrideOrImplement(psiClass, baseMethod, substitutor);
641 List<PsiGenerationInfo<PsiMethod>> results = GenerateMembersUtil.insertMembersBeforeAnchor(psiClass, anchor, prototypes);
643 PsiFile psiFile = psiClass.getContainingFile();
644 Editor editor = fileEditorManager.openTextEditor(new OpenFileDescriptor(psiFile.getProject(), psiFile.getVirtualFile()), false);
645 if (editor == null) return;
647 GenerateMembersUtil.positionCaret(editor, results.get(0).getPsiMember(), true);
648 editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
651 public static PsiClass getContextClass(Project project, Editor editor, PsiFile file, boolean allowInterface) {
652 PsiDocumentManager.getInstance(project).commitAllDocuments();
654 int offset = editor.getCaretModel().getOffset();
655 PsiElement element = file.findElementAt(offset);
656 do {
657 element = PsiTreeUtil.getParentOfType(element, PsiClass.class);
659 while (element instanceof PsiTypeParameter);
661 final PsiClass aClass = (PsiClass)element;
662 if (aClass instanceof JspClass) return null;
663 return aClass == null ||
664 !allowInterface && aClass.isInterface() ? null : aClass;
667 private static PsiSubstitutor getContextSubstitutor(PsiClass aClass) {
668 if (aClass instanceof PsiAnonymousClass) {
669 return ((PsiAnonymousClass)aClass).getBaseClassType().resolveGenerics().getSubstitutor();
672 return PsiSubstitutor.EMPTY;
675 public static void overrideOrImplementMethodsInRightPlace(Editor editor1, PsiClass aClass, Collection<PsiMethodMember> members, boolean copyJavadoc) {
676 boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
677 overrideOrImplementMethodsInRightPlace(editor1, aClass, members, copyJavadoc, insert);
680 public static List<PsiMethod> overrideOrImplementMethodCandidates(PsiClass aClass, Collection<CandidateInfo> candidatesToImplement,
681 boolean copyJavadoc) throws IncorrectOperationException {
682 boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION;
683 return overrideOrImplementMethodCandidates(aClass, candidatesToImplement, copyJavadoc, insert);