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.
18 * Created by IntelliJ IDEA.
22 * To change this template use Options | File Templates.
24 package com
.intellij
.codeInsight
.daemon
.impl
.quickfix
;
26 import com
.intellij
.codeInsight
.CodeInsightUtilBase
;
27 import com
.intellij
.codeInsight
.TargetElementUtil
;
28 import com
.intellij
.codeInsight
.daemon
.QuickFixBundle
;
29 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightInfo
;
30 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightUtil
;
31 import com
.intellij
.codeInsight
.intention
.IntentionAction
;
32 import com
.intellij
.find
.FindManager
;
33 import com
.intellij
.find
.findUsages
.FindUsagesHandler
;
34 import com
.intellij
.find
.findUsages
.FindUsagesManager
;
35 import com
.intellij
.find
.findUsages
.FindUsagesOptions
;
36 import com
.intellij
.find
.impl
.FindManagerImpl
;
37 import com
.intellij
.ide
.util
.SuperMethodWarningUtil
;
38 import com
.intellij
.ide
.DataManager
;
39 import com
.intellij
.openapi
.application
.ApplicationManager
;
40 import com
.intellij
.openapi
.command
.undo
.UndoUtil
;
41 import com
.intellij
.openapi
.editor
.Editor
;
42 import com
.intellij
.openapi
.progress
.ProgressManager
;
43 import com
.intellij
.openapi
.project
.Project
;
44 import com
.intellij
.openapi
.util
.TextRange
;
45 import com
.intellij
.psi
.*;
46 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
47 import com
.intellij
.psi
.codeStyle
.SuggestedNameInfo
;
48 import com
.intellij
.psi
.codeStyle
.VariableKind
;
49 import com
.intellij
.psi
.util
.PsiUtil
;
50 import com
.intellij
.psi
.util
.TypeConversionUtil
;
51 import com
.intellij
.refactoring
.RefactoringBundle
;
52 import com
.intellij
.refactoring
.changeSignature
.ChangeSignatureDialog
;
53 import com
.intellij
.refactoring
.changeSignature
.ChangeSignatureProcessor
;
54 import com
.intellij
.refactoring
.changeSignature
.ParameterInfoImpl
;
55 import com
.intellij
.refactoring
.util
.RefactoringUtil
;
56 import com
.intellij
.usageView
.UsageInfo
;
57 import com
.intellij
.util
.IncorrectOperationException
;
58 import com
.intellij
.util
.Processor
;
59 import org
.jetbrains
.annotations
.NonNls
;
60 import org
.jetbrains
.annotations
.NotNull
;
64 public class ChangeMethodSignatureFromUsageFix
implements IntentionAction
{
65 private final PsiMethod myTargetMethod
;
66 private final PsiExpression
[] myExpressions
;
67 private final PsiSubstitutor mySubstitutor
;
68 private final PsiElement myContext
;
69 private final boolean myChangeAllUsages
;
70 private final int myMinUsagesNumberToShowDialog
;
71 private ParameterInfoImpl
[] myNewParametersInfo
;
73 ChangeMethodSignatureFromUsageFix(@NotNull PsiMethod targetMethod
,
74 @NotNull PsiExpression
[] expressions
,
75 @NotNull PsiSubstitutor substitutor
,
76 @NotNull PsiElement context
,
77 boolean changeAllUsages
, int minUsagesNumberToShowDialog
) {
78 myTargetMethod
= targetMethod
;
79 myExpressions
= expressions
;
80 mySubstitutor
= substitutor
;
82 myChangeAllUsages
= changeAllUsages
;
83 myMinUsagesNumberToShowDialog
= minUsagesNumberToShowDialog
;
87 public String
getText() {
88 return QuickFixBundle
.message("change.method.signature.from.usage.text",
89 HighlightUtil
.formatMethod(myTargetMethod
),
90 myTargetMethod
.getName(),
91 formatTypesList(myNewParametersInfo
, myContext
));
94 private static String
formatTypesList(ParameterInfoImpl
[] infos
, PsiElement context
) {
97 for (ParameterInfoImpl info
: infos
) {
98 PsiType type
= info
.getTypeWrapper().getType(context
, context
.getManager());
99 if (result
.length() != 0) {
102 result
+= type
.getPresentableText();
105 catch (IncorrectOperationException e
) {
112 public String
getFamilyName() {
113 return QuickFixBundle
.message("change.method.signature.from.usage.family");
116 public boolean isAvailable(@NotNull Project project
, Editor editor
, PsiFile file
) {
117 if (!myTargetMethod
.isValid()) return false;
118 for (PsiExpression expression
: myExpressions
) {
119 if (!expression
.isValid()) return false;
122 myNewParametersInfo
= getNewParametersInfo(myExpressions
, myTargetMethod
, mySubstitutor
);
123 if (myNewParametersInfo
== null || formatTypesList(myNewParametersInfo
, myContext
) == null) return false;
124 return !isMethodSignatureExists();
127 private boolean isMethodSignatureExists() {
128 PsiClass target
= myTargetMethod
.getContainingClass();
129 PsiMethod
[] methods
= target
.findMethodsByName(myTargetMethod
.getName(), false);
130 for (PsiMethod method
: methods
) {
131 if (PsiUtil
.isApplicable(method
, PsiSubstitutor
.EMPTY
, myExpressions
)) return true;
136 public void invoke(@NotNull final Project project
, Editor editor
, final PsiFile file
) {
137 if (!CodeInsightUtilBase
.prepareFileForWrite(file
)) return;
139 final PsiMethod method
= SuperMethodWarningUtil
.checkSuperMethod(myTargetMethod
, RefactoringBundle
.message("to.refactor"));
140 if (method
== null) return;
141 if (!CodeInsightUtilBase
.prepareFileForWrite(method
.getContainingFile())) return;
143 final FindUsagesManager findUsagesManager
= ((FindManagerImpl
)FindManager
.getInstance(project
)).getFindUsagesManager();
144 final FindUsagesHandler handler
= findUsagesManager
.getFindUsagesHandler(method
, false);
145 if (handler
== null) return; //on failure or cancel (e.g. cancel of super methods dialog)
147 final FindUsagesOptions options
= new FindUsagesOptions(project
, editor
== null?
null : DataManager
.getInstance().getDataContext(editor
.getComponent()));
148 options
.isImplementingMethods
= true;
149 options
.isMethodsUsages
= true;
150 options
.isOverridingMethods
= true;
151 options
.isUsages
= true;
152 options
.isSearchForTextOccurences
= false;
153 final int[] usagesFound
= new int[1];
154 Runnable runnable
= new Runnable() {
156 Processor
<UsageInfo
> processor
= new Processor
<UsageInfo
>() {
157 public boolean process(final UsageInfo t
) {
158 return ++usagesFound
[0] < myMinUsagesNumberToShowDialog
;
162 handler
.processElementUsages(method
, processor
, options
);
165 String progressTitle
= QuickFixBundle
.message("searching.for.usages.progress.title");
166 if (!ProgressManager
.getInstance().runProcessWithProgressSynchronously(runnable
, progressTitle
, true, project
)) return;
168 myNewParametersInfo
= getNewParametersInfo(myExpressions
, myTargetMethod
, mySubstitutor
);
169 if (ApplicationManager
.getApplication().isUnitTestMode() || usagesFound
[0] < myMinUsagesNumberToShowDialog
) {
170 ChangeSignatureProcessor processor
= new ChangeSignatureProcessor(
175 method
.getReturnType(),
176 myNewParametersInfo
){
178 protected UsageInfo
[] findUsages() {
179 return myChangeAllUsages ?
super.findUsages() : UsageInfo
.EMPTY_ARRAY
;
183 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
185 UndoUtil
.markPsiFileForUndo(file
);
190 List
<ParameterInfoImpl
> parameterInfos
= new ArrayList
<ParameterInfoImpl
>(Arrays
.asList(myNewParametersInfo
));
191 final PsiReferenceExpression refExpr
= TargetElementUtil
.findReferenceExpression(editor
);
192 ChangeSignatureDialog dialog
= new ChangeSignatureDialog(project
, method
, false, refExpr
);
193 dialog
.setParameterInfos(parameterInfos
);
195 myNewParametersInfo
= dialog
.getParameters();
199 public String
getNewParameterNameByOldIndex(int oldIndex
) {
200 if (myNewParametersInfo
== null) return null;
201 for (ParameterInfoImpl info
: myNewParametersInfo
) {
202 if (info
.oldParameterIndex
== oldIndex
) {
203 return info
.getName();
209 private static ParameterInfoImpl
[] getNewParametersInfo(PsiExpression
[] expressions
,
210 PsiMethod targetMethod
,
211 PsiSubstitutor substitutor
) {
212 PsiParameter
[] parameters
= targetMethod
.getParameterList().getParameters();
213 List
<ParameterInfoImpl
> result
= new ArrayList
<ParameterInfoImpl
>();
214 if (expressions
.length
< parameters
.length
) {
215 // find which parameters to remove
219 while (ei
< expressions
.length
&& pi
< parameters
.length
) {
220 PsiExpression expression
= expressions
[ei
];
221 PsiParameter parameter
= parameters
[pi
];
222 PsiType paramType
= substitutor
.substitute(parameter
.getType());
223 if (TypeConversionUtil
.areTypesAssignmentCompatible(paramType
, expression
)) {
224 result
.add(new ParameterInfoImpl(pi
, parameter
.getName(), PsiUtil
.convertAnonymousToBaseType(paramType
)));
232 if (result
.size() != expressions
.length
) return null;
234 else if (expressions
.length
> parameters
.length
) {
235 // find which parameters to introduce and where
236 Set
<String
> existingNames
= new HashSet
<String
>();
237 for (PsiParameter parameter
: parameters
) {
238 existingNames
.add(parameter
.getName());
242 while (ei
< expressions
.length
|| pi
< parameters
.length
) {
243 PsiExpression expression
= ei
< expressions
.length ? expressions
[ei
] : null;
244 PsiParameter parameter
= pi
< parameters
.length ? parameters
[pi
] : null;
245 PsiType paramType
= parameter
== null ?
null : substitutor
.substitute(parameter
.getType());
246 boolean parameterAssignable
= paramType
!= null && (expression
== null || TypeConversionUtil
.areTypesAssignmentCompatible(paramType
, expression
));
247 if (parameterAssignable
) {
248 result
.add(new ParameterInfoImpl(pi
, parameter
.getName(), parameter
.getType()));
252 else if (expression
!= null) {
253 PsiType exprType
= RefactoringUtil
.getTypeByExpression(expression
);
254 if (exprType
== null) return null;
255 JavaCodeStyleManager codeStyleManager
= JavaCodeStyleManager
.getInstance(expression
.getProject());
256 String name
= suggestUniqueParameterName(codeStyleManager
, expression
, exprType
, existingNames
);
257 result
.add(new ParameterInfoImpl(-1, name
, exprType
, expression
.getText().replace('\n', ' ')));
261 if (result
.size() != expressions
.length
) return null;
264 //parameter type changed
265 for (int i
= 0; i
< parameters
.length
; i
++) {
266 PsiParameter parameter
= parameters
[i
];
267 PsiExpression expression
= expressions
[i
];
268 PsiType paramType
= substitutor
.substitute(parameter
.getType());
269 if (TypeConversionUtil
.areTypesAssignmentCompatible(paramType
, expression
)) {
270 result
.add(new ParameterInfoImpl(i
, parameter
.getName(), paramType
));
273 PsiType exprType
= RefactoringUtil
.getTypeByExpression(expression
);
274 if (exprType
== null) return null;
275 result
.add(new ParameterInfoImpl(i
, parameter
.getName(), exprType
));
278 // do not perform silly refactorings
279 boolean isSilly
= true;
280 for (int i
= 0; i
< result
.size(); i
++) {
281 PsiParameter parameter
= parameters
[i
];
282 PsiType paramType
= substitutor
.substitute(parameter
.getType());
283 ParameterInfoImpl parameterInfo
= result
.get(i
);
284 String typeText
= parameterInfo
.getTypeText();
285 if (!paramType
.equalsToText(typeText
)) {
290 if (isSilly
) return null;
292 return result
.toArray(new ParameterInfoImpl
[result
.size()]);
295 private static String
suggestUniqueParameterName(JavaCodeStyleManager codeStyleManager
,
296 PsiExpression expression
,
298 Set
<String
> existingNames
) {
299 SuggestedNameInfo nameInfo
= codeStyleManager
.suggestVariableName(VariableKind
.PARAMETER
, null, expression
, exprType
);
300 @NonNls String
[] names
= nameInfo
.names
;
301 if (names
.length
== 0) names
= new String
[]{"param"};
304 for (String name
: names
) {
305 String suggested
= name
+ (suffix
== 0 ?
"" : String
.valueOf(suffix
));
306 if (existingNames
.add(suggested
)) {
314 public static void registerIntentions(@NotNull JavaResolveResult
[] candidates
,
315 @NotNull PsiExpressionList list
,
316 @NotNull HighlightInfo highlightInfo
,
317 TextRange fixRange
) {
318 if (candidates
.length
== 0) return;
319 PsiExpression
[] expressions
= list
.getExpressions();
320 for (JavaResolveResult candidate
: candidates
) {
321 registerIntention(expressions
, highlightInfo
, fixRange
, candidate
, list
);
325 private static void registerIntention(@NotNull PsiExpression
[] expressions
,
326 @NotNull HighlightInfo highlightInfo
,
328 @NotNull JavaResolveResult candidate
,
329 @NotNull PsiElement context
) {
330 if (!candidate
.isStaticsScopeCorrect()) return;
331 PsiMethod method
= (PsiMethod
)candidate
.getElement();
332 PsiSubstitutor substitutor
= candidate
.getSubstitutor();
333 if (method
!= null && context
.getManager().isInProject(method
)) {
334 ChangeMethodSignatureFromUsageFix fix
= new ChangeMethodSignatureFromUsageFix(method
, expressions
, substitutor
, context
, false, 2);
335 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, fix
, null);
339 public boolean startInWriteAction() {