update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / quickfix / ChangeMethodSignatureFromUsageFix.java
blobf66231688ff9d077b742a2106e965d86f9fbce81
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /**
18 * Created by IntelliJ IDEA.
19 * User: cdr
20 * Date: Nov 13, 2002
21 * Time: 3:26:50 PM
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;
62 import java.util.*;
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;
81 myContext = context;
82 myChangeAllUsages = changeAllUsages;
83 myMinUsagesNumberToShowDialog = minUsagesNumberToShowDialog;
86 @NotNull
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) {
95 String result = "";
96 try {
97 for (ParameterInfoImpl info : infos) {
98 PsiType type = info.getTypeWrapper().getType(context, context.getManager());
99 if (result.length() != 0) {
100 result += ", ";
102 result += type.getPresentableText();
105 catch (IncorrectOperationException e) {
106 return null;
108 return result;
111 @NotNull
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;
133 return false;
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() {
155 public void run() {
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(
171 project,
172 method,
173 false, null,
174 method.getName(),
175 method.getReturnType(),
176 myNewParametersInfo){
177 @NotNull
178 protected UsageInfo[] findUsages() {
179 return myChangeAllUsages ? super.findUsages() : UsageInfo.EMPTY_ARRAY;
182 processor.run();
183 ApplicationManager.getApplication().runWriteAction(new Runnable() {
184 public void run() {
185 UndoUtil.markPsiFileForUndo(file);
189 else {
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);
194 dialog.show();
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();
206 return null;
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
216 int ei = 0;
217 int pi = 0;
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)));
225 pi++;
226 ei++;
228 else {
229 pi++;
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());
240 int ei = 0;
241 int pi = 0;
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()));
249 pi++;
250 ei++;
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', ' ')));
258 ei++;
261 if (result.size() != expressions.length) return null;
263 else {
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));
272 else {
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)) {
286 isSilly = false;
287 break;
290 if (isSilly) return null;
292 return result.toArray(new ParameterInfoImpl[result.size()]);
295 private static String suggestUniqueParameterName(JavaCodeStyleManager codeStyleManager,
296 PsiExpression expression,
297 PsiType exprType,
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"};
302 int suffix = 0;
303 while (true) {
304 for (String name : names) {
305 String suggested = name + (suffix == 0 ? "" : String.valueOf(suffix));
306 if (existingNames.add(suggested)) {
307 return suggested;
310 suffix++;
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,
327 TextRange fixRange,
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() {
340 return false;