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
;
67 import java
.awt
.event
.ActionEvent
;
68 import java
.awt
.event
.InputEvent
;
69 import java
.awt
.event
.KeyEvent
;
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() {
81 public static Collection
<CandidateInfo
> getMethodsToOverrideImplement(PsiClass aClass
, boolean toImplement
) {
82 return getMapToOverrideImplement(aClass
, toImplement
).values();
86 public static Collection
<MethodSignature
> getMethodSignaturesToImplement(@NotNull PsiClass aClass
) {
87 return getMapToOverrideImplement(aClass
, true).keySet();
91 public static Collection
<MethodSignature
> getMethodSignaturesToOverride(@NotNull PsiClass aClass
) {
92 return getMapToOverrideImplement(aClass
, false).keySet();
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())) {
113 if (MethodSignatureUtil
.findMethodBySignature(aClass
, signature
, false) != null) continue;
115 if (method
.hasModifierProperty(PsiModifier
.FINAL
)) {
116 finals
.put(signature
, method
);
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
);
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
);
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
);
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
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
)) {
214 if (PsiUtil
.isLanguageLevel6OrHigher(targetClass
)) return true;
215 if (targetClass
.isInterface()) return true;
216 PsiClass superClass
= superMethod
.getContainingClass();
217 return !superClass
.isInterface();
221 private static Collection
<PsiMethod
> overrideOrImplementMethod(PsiClass aClass
,
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
);
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);
257 PsiDocComment comment
= result
.getDocComment();
258 if (comment
!= null){
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
);
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) {
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
);
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
);
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
));
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
);
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()){
366 buffer
.append(superMethod
.getName());
369 for (int i
= 0; i
< parms
.length
; i
++) {
370 String name
= parms
[i
].getName();
371 if (i
> 0) 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
;
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) {
416 m
= factory
.createMethodFromText(methodText
, originalMethod
);
418 catch (IncorrectOperationException e
) {
419 ApplicationManager
.getApplication().invokeLater(new Runnable() {
421 Messages
.showErrorDialog(CodeInsightBundle
.message("override.implement.broken.file.template.message"),
422 CodeInsightBundle
.message("override.implement.broken.file.template.title"));
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
,
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
)) {
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")) {
473 public boolean isSelected(AnActionEvent e
) {
474 return merge
.get().booleanValue();
478 public void setSelected(AnActionEvent e
, boolean 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);
494 chooser
.selectElements(isAll ? all
: onlyPrimary
);
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());
512 private static String
getChooserTitle(boolean toImplement
, Ref
<Boolean
> merge
) {
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() {
551 final CodeInsightActionHandler handler
= toImplement ?
new OverrideMethodsHandler(): new ImplementMethodsHandler();
552 handler
.invoke(project
, editor
, aClass
.getContainingFile());
561 public static void overrideOrImplementMethodsInRightPlace(Editor editor
,
563 Collection
<PsiMethodMember
> candidates
,
565 boolean insertOverrideWherePossible
) {
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
));
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
){
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
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();
621 MethodSignature signature
= MethodSignatureUtil
.createMethodSignature(name
, nextBaseMethod
.getParameterList(), nextBaseMethod
.getTypeParameterList(), substitutor
);
622 PsiMethod nextMethod
= MethodSignatureUtil
.findMethodBySignature(aClass
, signature
, false);
623 if (nextMethod
!= null){
627 nextBaseMethod
= PsiTreeUtil
.getNextSiblingOfType(nextBaseMethod
, PsiMethod
.class);
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
);
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
);