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
;
53 import java
.awt
.event
.ActionEvent
;
54 import java
.awt
.event
.InputEvent
;
55 import java
.awt
.event
.KeyEvent
;
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() {
67 public static Collection
<CandidateInfo
> getMethodsToOverrideImplement(PsiClass aClass
, boolean toImplement
) {
68 return getMapToOverrideImplement(aClass
, toImplement
).values();
72 public static Collection
<MethodSignature
> getMethodSignaturesToImplement(@NotNull PsiClass aClass
) {
73 return getMapToOverrideImplement(aClass
, true).keySet();
77 public static Collection
<MethodSignature
> getMethodSignaturesToOverride(@NotNull PsiClass aClass
) {
78 return getMapToOverrideImplement(aClass
, false).keySet();
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())) {
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
,
116 if (MethodSignatureUtil
.findMethodBySignature(aClass
, signature
, false) != null) continue;
118 if (method
.hasModifierProperty(PsiModifier
.FINAL
)) {
119 finals
.put(signature
, method
);
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
);
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
);
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
);
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
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
)) {
211 if (PsiUtil
.isLanguageLevel6OrHigher(targetClass
)) return true;
212 if (targetClass
.isInterface()) return true;
213 PsiClass superClass
= superMethod
.getContainingClass();
214 return !superClass
.isInterface();
218 private static Collection
<PsiMethod
> overrideOrImplementMethod(PsiClass aClass
,
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
);
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);
254 PsiDocComment comment
= result
.getDocComment();
255 if (comment
!= null){
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
);
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) {
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
);
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
);
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
));
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
);
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()){
363 buffer
.append(superMethod
.getName());
366 for (int i
= 0; i
< parms
.length
; i
++) {
367 String name
= parms
[i
].getName();
368 if (i
> 0) 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
;
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) {
413 m
= factory
.createMethodFromText(methodText
, originalMethod
);
415 catch (IncorrectOperationException e
) {
416 ApplicationManager
.getApplication().invokeLater(new Runnable() {
418 Messages
.showErrorDialog(CodeInsightBundle
.message("override.implement.broken.file.template.message"),
419 CodeInsightBundle
.message("override.implement.broken.file.template.title"));
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
,
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
)) {
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")) {
470 public boolean isSelected(AnActionEvent e
) {
471 return merge
.get().booleanValue();
475 public void setSelected(AnActionEvent e
, boolean 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);
491 chooser
.selectElements(isAll ? all
: onlyPrimary
);
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());
509 private static String
getChooserTitle(boolean toImplement
, Ref
<Boolean
> merge
) {
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() {
548 final CodeInsightActionHandler handler
= toImplement ?
new OverrideMethodsHandler(): new ImplementMethodsHandler();
549 handler
.invoke(project
, editor
, aClass
.getContainingFile());
558 public static void overrideOrImplementMethodsInRightPlace(Editor editor
,
560 Collection
<PsiMethodMember
> candidates
,
562 boolean insertOverrideWherePossible
) {
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
));
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
){
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
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();
618 MethodSignature signature
= MethodSignatureUtil
.createMethodSignature(name
, nextBaseMethod
.getParameterList(), nextBaseMethod
.getTypeParameterList(), substitutor
);
619 PsiMethod nextMethod
= MethodSignatureUtil
.findMethodBySignature(aClass
, signature
, false);
620 if (nextMethod
!= null){
624 nextBaseMethod
= PsiTreeUtil
.getNextSiblingOfType(nextBaseMethod
, PsiMethod
.class);
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
);
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
);