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
;
52 import java
.awt
.event
.ActionEvent
;
53 import java
.awt
.event
.KeyEvent
;
54 import java
.awt
.event
.InputEvent
;
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() {
66 public static Collection
<CandidateInfo
> getMethodsToOverrideImplement(PsiClass aClass
, boolean toImplement
) {
67 return getMapToOverrideImplement(aClass
, toImplement
).values();
71 public static Collection
<MethodSignature
> getMethodSignaturesToImplement(@NotNull PsiClass aClass
) {
72 return getMapToOverrideImplement(aClass
, true).keySet();
76 public static Collection
<MethodSignature
> getMethodSignaturesToOverride(@NotNull PsiClass aClass
) {
77 return getMapToOverrideImplement(aClass
, false).keySet();
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())) {
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
,
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 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
);
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
);
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
);
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
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
)) {
206 if (PsiUtil
.isLanguageLevel6OrHigher(targetClass
)) return true;
207 if (targetClass
.isInterface()) return true;
208 PsiClass superClass
= superMethod
.getContainingClass();
209 return !superClass
.isInterface();
213 private static Collection
<PsiMethod
> overrideOrImplementMethod(PsiClass aClass
,
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
);
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);
249 PsiDocComment comment
= result
.getDocComment();
250 if (comment
!= null){
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
);
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) {
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
);
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
);
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
));
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
);
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()){
358 buffer
.append(superMethod
.getName());
361 for (int i
= 0; i
< parms
.length
; i
++) {
362 String name
= parms
[i
].getName();
363 if (i
> 0) 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
;
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) {
408 m
= factory
.createMethodFromText(methodText
, originalMethod
);
410 catch (IncorrectOperationException e
) {
411 ApplicationManager
.getApplication().invokeLater(new Runnable() {
413 Messages
.showErrorDialog(CodeInsightBundle
.message("override.implement.broken.file.template.message"),
414 CodeInsightBundle
.message("override.implement.broken.file.template.title"));
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
,
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
)) {
459 protected void fillToolbarActions(DefaultActionGroup group
) {
460 super.fillToolbarActions(group
);
461 if (toImplement
) return;
463 final ToggleAction mergeAction
= new ToggleAction("Show methods to implement") {
465 public boolean isSelected(AnActionEvent e
) {
466 return merge
.get().booleanValue();
470 public void setSelected(AnActionEvent e
, boolean 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
);
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());
500 private static String
getChooserTitle(boolean toImplement
, Ref
<Boolean
> merge
) {
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() {
539 final CodeInsightActionHandler handler
= toImplement ?
new OverrideMethodsHandler(): new ImplementMethodsHandler();
540 handler
.invoke(project
, editor
, aClass
.getContainingFile());
549 public static void overrideOrImplementMethodsInRightPlace(Editor editor
,
551 Collection
<PsiMethodMember
> candidates
,
553 boolean insertOverrideWherePossible
) {
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
));
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
){
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
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();
609 MethodSignature signature
= MethodSignatureUtil
.createMethodSignature(name
, nextBaseMethod
.getParameterList(), nextBaseMethod
.getTypeParameterList(), substitutor
);
610 PsiMethod nextMethod
= MethodSignatureUtil
.findMethodBySignature(aClass
, signature
, false);
611 if (nextMethod
!= null){
615 nextBaseMethod
= PsiTreeUtil
.getNextSiblingOfType(nextBaseMethod
, PsiMethod
.class);
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
);
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
);