change signature: setup default type value as default type; setup selection
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / changeSignature / JavaChangeSignatureUsageProcessor.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.refactoring.changeSignature;
17
18 import com.intellij.codeInsight.ExceptionUtil;
19 import com.intellij.lang.StdLanguages;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.util.Ref;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.psi.*;
26 import com.intellij.psi.codeStyle.CodeStyleManager;
27 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
28 import com.intellij.psi.codeStyle.VariableKind;
29 import com.intellij.psi.scope.processor.VariablesProcessor;
30 import com.intellij.psi.scope.util.PsiScopesUtil;
31 import com.intellij.psi.util.*;
32 import com.intellij.refactoring.RefactoringBundle;
33 import com.intellij.refactoring.rename.RenameUtil;
34 import com.intellij.refactoring.util.*;
35 import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
36 import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo;
37 import com.intellij.usageView.UsageInfo;
38 import com.intellij.util.IncorrectOperationException;
39 import com.intellij.util.VisibilityUtil;
40 import com.intellij.util.containers.ContainerUtil;
41 import com.intellij.util.containers.HashSet;
42 import com.intellij.util.containers.MultiMap;
43 import org.jetbrains.annotations.Nullable;
44
45 import java.util.*;
46
47 /**
48  * @author Maxim.Medvedev
49  */
50 public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
51   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeSignature.JavaChangeSignatureUsageProcessor");
52
53   private static boolean isJavaUsage(UsageInfo info) {
54     final PsiElement element = info.getElement();
55     if (element == null) return false;
56     return element.getLanguage() == StdLanguages.JAVA;
57   }
58
59   public UsageInfo[] findUsages(ChangeInfo info) {
60     if (info instanceof JavaChangeInfo) {
61       return new JavaChangeSignatureUsageSearcher((JavaChangeInfo)info).findUsages();
62     }
63     else {
64       return UsageInfo.EMPTY_ARRAY;
65     }
66   }
67
68   public MultiMap<PsiElement, String> findConflicts(ChangeInfo info, Ref<UsageInfo[]> refUsages) {
69     if (info instanceof JavaChangeInfo) {
70       return new ConflictSearcher((JavaChangeInfo)info).findConflicts(refUsages);
71     }
72     else {
73       return new MultiMap<PsiElement, String>();
74     }
75   }
76
77   public boolean processUsage(ChangeInfo changeInfo, UsageInfo usage, boolean beforeMethodChange, UsageInfo[] usages) {
78     if (!isJavaUsage(usage)) return false;
79     if (!(changeInfo instanceof JavaChangeInfo)) return false;
80
81
82     if (beforeMethodChange) {
83       if (usage instanceof CallerUsageInfo) {
84         final CallerUsageInfo callerUsageInfo = (CallerUsageInfo)usage;
85         processCallerMethod((JavaChangeInfo)changeInfo, callerUsageInfo.getMethod(), null, callerUsageInfo.isToInsertParameter(),
86                             callerUsageInfo.isToInsertException());
87         return true;
88       }
89       else if (usage instanceof OverriderUsageInfo) {
90         OverriderUsageInfo info = (OverriderUsageInfo)usage;
91         final PsiMethod method = info.getElement();
92         final PsiMethod baseMethod = info.getBaseMethod();
93         if (info.isOriginalOverrider()) {
94           processPrimaryMethod((JavaChangeInfo)changeInfo, method, baseMethod, false);
95         }
96         else {
97           processCallerMethod((JavaChangeInfo)changeInfo, method, baseMethod, info.isToInsertArgs(), info.isToCatchExceptions());
98         }
99         return true;
100       }
101
102     }
103     else {
104       PsiElement element = usage.getElement();
105       LOG.assertTrue(element != null);
106
107       if (usage instanceof DefaultConstructorImplicitUsageInfo) {
108         final DefaultConstructorImplicitUsageInfo defConstructorUsage = (DefaultConstructorImplicitUsageInfo)usage;
109         PsiMethod constructor = defConstructorUsage.getConstructor();
110         if (!constructor.isPhysical()) {
111           final boolean toPropagate =
112             changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.remove(constructor);
113           final PsiClass containingClass = defConstructorUsage.getContainingClass();
114           constructor = (PsiMethod)containingClass.add(constructor);
115           PsiUtil.setModifierProperty(constructor, VisibilityUtil.getVisibilityModifier(containingClass.getModifierList()), true);
116           if (toPropagate) {
117             ((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.add(constructor);
118           }
119         }
120         addSuperCall((JavaChangeInfo)changeInfo, constructor, defConstructorUsage.getBaseConstructor(), usages);
121         return true;
122       }
123       else if (usage instanceof NoConstructorClassUsageInfo) {
124         addDefaultConstructor(((JavaChangeInfo)changeInfo), ((NoConstructorClassUsageInfo)usage).getPsiClass(), usages);
125         return true;
126       }
127       else if (usage instanceof MethodCallUsageInfo) {
128         final MethodCallUsageInfo methodCallInfo = (MethodCallUsageInfo)usage;
129         processMethodUsage(methodCallInfo.getElement(), (JavaChangeInfo)changeInfo, methodCallInfo.isToChangeArguments(),
130                            methodCallInfo.isToCatchExceptions(), methodCallInfo.getReferencedMethod(), methodCallInfo.getSubstitutor(), usages);
131         return true;
132       }
133       else if (usage instanceof ChangeSignatureParameterUsageInfo) {
134         String newName = ((ChangeSignatureParameterUsageInfo)usage).newParameterName;
135         String oldName = ((ChangeSignatureParameterUsageInfo)usage).oldParameterName;
136         processParameterUsage((PsiReferenceExpression)element, oldName, newName);
137         return true;
138       }
139       else if (usage instanceof CallReferenceUsageInfo) {
140         ((CallReferenceUsageInfo)usage).getReference().handleChangeSignature(changeInfo);
141         return true;
142       }
143       else if (element instanceof PsiEnumConstant) {
144         fixActualArgumentsList(((PsiEnumConstant)element).getArgumentList(), (JavaChangeInfo)changeInfo, true, PsiSubstitutor.EMPTY);
145         return true;
146       }
147       else if (!(usage instanceof OverriderUsageInfo)) {
148         PsiReference reference = usage instanceof MoveRenameUsageInfo ? usage.getReference() : element.getReference();
149         if (reference != null) {
150           PsiElement target = changeInfo.getMethod();
151           if (target != null) {
152             reference.bindToElement(target);
153           }
154         }
155       }
156     }
157     return false;
158   }
159
160   private static void processParameterUsage(PsiReferenceExpression ref, String oldName, String newName)
161     throws IncorrectOperationException {
162
163     PsiElement last = ref.getReferenceNameElement();
164     if (last instanceof PsiIdentifier && last.getText().equals(oldName)) {
165       PsiElementFactory factory = JavaPsiFacade.getInstance(ref.getProject()).getElementFactory();
166       PsiIdentifier newNameIdentifier = factory.createIdentifier(newName);
167       last.replace(newNameIdentifier);
168     }
169   }
170
171
172   private static void addDefaultConstructor(JavaChangeInfo changeInfo, PsiClass aClass, final UsageInfo[] usages)
173     throws IncorrectOperationException {
174     if (!(aClass instanceof PsiAnonymousClass)) {
175       PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject());
176       PsiMethod defaultConstructor = factory.createMethodFromText(aClass.getName() + "(){}", aClass);
177       defaultConstructor = (PsiMethod)CodeStyleManager.getInstance(aClass.getProject()).reformat(defaultConstructor);
178       defaultConstructor = (PsiMethod)aClass.add(defaultConstructor);
179       PsiUtil.setModifierProperty(defaultConstructor, VisibilityUtil.getVisibilityModifier(aClass.getModifierList()), true);
180       addSuperCall(changeInfo, defaultConstructor, null, usages);
181     }
182     else {
183       final PsiElement parent = aClass.getParent();
184       if (parent instanceof PsiNewExpression) {
185         final PsiExpressionList argumentList = ((PsiNewExpression)parent).getArgumentList();
186         final PsiClass baseClass = changeInfo.getMethod().getContainingClass();
187         final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, aClass, PsiSubstitutor.EMPTY);
188         fixActualArgumentsList(argumentList, changeInfo, true, substitutor);
189       }
190     }
191   }
192
193   private static void addSuperCall(JavaChangeInfo changeInfo, PsiMethod constructor, PsiMethod callee, final UsageInfo[] usages)
194     throws IncorrectOperationException {
195     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(constructor.getProject());
196     PsiExpressionStatement superCall = (PsiExpressionStatement)factory.createStatementFromText("super();", constructor);
197     PsiCodeBlock body = constructor.getBody();
198     assert body != null;
199     PsiStatement[] statements = body.getStatements();
200     if (statements.length > 0) {
201       superCall = (PsiExpressionStatement)body.addBefore(superCall, statements[0]);
202     }
203     else {
204       superCall = (PsiExpressionStatement)body.add(superCall);
205     }
206     PsiMethodCallExpression callExpression = (PsiMethodCallExpression)superCall.getExpression();
207     final PsiClass aClass = constructor.getContainingClass();
208     final PsiClass baseClass = changeInfo.getMethod().getContainingClass();
209     final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, aClass, PsiSubstitutor.EMPTY);
210     processMethodUsage(callExpression.getMethodExpression(), changeInfo, true, false, callee, substitutor, usages);
211   }
212
213   private static void processMethodUsage(PsiElement ref,
214                                   JavaChangeInfo changeInfo,
215                                   boolean toChangeArguments,
216                                   boolean toCatchExceptions,
217                                   PsiMethod callee, PsiSubstitutor subsitutor, final UsageInfo[] usages) throws IncorrectOperationException {
218     if (changeInfo.isNameChanged()) {
219       if (ref instanceof PsiJavaCodeReferenceElement) {
220         PsiElement last = ((PsiJavaCodeReferenceElement)ref).getReferenceNameElement();
221         if (last instanceof PsiIdentifier && last.getText().equals(changeInfo.getOldName())) {
222           last.replace(changeInfo.getNewNameIdentifier());
223         }
224       }
225     }
226
227     final PsiMethod caller = RefactoringUtil.getEnclosingMethod(ref);
228     if (toChangeArguments) {
229       final PsiExpressionList list = RefactoringUtil.getArgumentListByMethodReference(ref);
230       boolean toInsertDefaultValue = needDefaultValue(changeInfo, caller);
231       if (toInsertDefaultValue && ref instanceof PsiReferenceExpression) {
232         final PsiExpression qualifierExpression = ((PsiReferenceExpression)ref).getQualifierExpression();
233         if (qualifierExpression instanceof PsiSuperExpression && callerSignatureIsAboutToChangeToo(caller, usages)) {
234           toInsertDefaultValue = false;
235         }
236       }
237
238       fixActualArgumentsList(list, changeInfo, toInsertDefaultValue, subsitutor);
239     }
240
241     if (toCatchExceptions) {
242       if (!(ref instanceof PsiReferenceExpression &&
243             ((PsiReferenceExpression)ref).getQualifierExpression() instanceof PsiSuperExpression)) {
244         if (needToCatchExceptions(changeInfo, caller)) {
245           PsiClassType[] newExceptions =
246             callee != null ? getCalleeChangedExceptionInfo(callee) : getPrimaryChangedExceptionInfo(changeInfo);
247           fixExceptions(ref, newExceptions);
248         }
249       }
250     }
251   }
252
253   private static boolean callerSignatureIsAboutToChangeToo(final PsiMethod caller, final UsageInfo[] usages) {
254     for (UsageInfo usage : usages) {
255       if (usage instanceof MethodCallUsageInfo &&
256           MethodSignatureUtil.isSuperMethod(((MethodCallUsageInfo)usage).getReferencedMethod(), caller)) {
257         return true;
258       }
259     }
260     return false;
261   }
262
263   private static PsiClassType[] getCalleeChangedExceptionInfo(final PsiMethod callee) {
264     return callee.getThrowsList().getReferencedTypes(); //Callee method's throws list is already modified!
265   }
266
267   private static void fixExceptions(PsiElement ref, PsiClassType[] newExceptions) throws IncorrectOperationException {
268     //methods' throws lists are already modified, may use ExceptionUtil.collectUnhandledExceptions
269     newExceptions = filterCheckedExceptions(newExceptions);
270
271     PsiElement context = PsiTreeUtil.getParentOfType(ref, PsiTryStatement.class, PsiMethod.class);
272     if (context instanceof PsiTryStatement) {
273       PsiTryStatement tryStatement = (PsiTryStatement)context;
274       PsiCodeBlock tryBlock = tryStatement.getTryBlock();
275
276       //Remove unused catches
277       Collection<PsiClassType> classes = ExceptionUtil.collectUnhandledExceptions(tryBlock, tryBlock);
278       PsiParameter[] catchParameters = tryStatement.getCatchBlockParameters();
279       for (PsiParameter parameter : catchParameters) {
280         final PsiType caughtType = parameter.getType();
281
282         if (!(caughtType instanceof PsiClassType)) continue;
283         if (ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)caughtType)) continue;
284
285         if (!isCatchParameterRedundant((PsiClassType)caughtType, classes)) continue;
286         parameter.getParent().delete(); //delete catch section
287       }
288
289       PsiClassType[] exceptionsToAdd = filterUnhandledExceptions(newExceptions, tryBlock);
290       addExceptions(exceptionsToAdd, tryStatement);
291
292       adjustPossibleEmptyTryStatement(tryStatement);
293     }
294     else {
295       newExceptions = filterUnhandledExceptions(newExceptions, ref);
296       if (newExceptions.length > 0) {
297         //Add new try statement
298         PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(ref.getProject());
299         PsiTryStatement tryStatement = (PsiTryStatement)elementFactory.createStatementFromText("try {} catch (Exception e) {}", null);
300         PsiStatement anchor = PsiTreeUtil.getParentOfType(ref, PsiStatement.class);
301         LOG.assertTrue(anchor != null);
302         tryStatement.getTryBlock().add(anchor);
303         tryStatement = (PsiTryStatement)anchor.getParent().addAfter(tryStatement, anchor);
304
305         addExceptions(newExceptions, tryStatement);
306         anchor.delete();
307         tryStatement.getCatchSections()[0].delete(); //Delete dummy catch section
308       }
309     }
310   }
311
312   private static PsiClassType[] filterCheckedExceptions(PsiClassType[] exceptions) {
313     List<PsiClassType> result = new ArrayList<PsiClassType>();
314     for (PsiClassType exceptionType : exceptions) {
315       if (!ExceptionUtil.isUncheckedException(exceptionType)) result.add(exceptionType);
316     }
317     return result.toArray(new PsiClassType[result.size()]);
318   }
319
320   private static void adjustPossibleEmptyTryStatement(PsiTryStatement tryStatement) throws IncorrectOperationException {
321     PsiCodeBlock tryBlock = tryStatement.getTryBlock();
322     if (tryBlock != null) {
323       if (tryStatement.getCatchSections().length == 0 &&
324           tryStatement.getFinallyBlock() == null) {
325         PsiElement firstBodyElement = tryBlock.getFirstBodyElement();
326         if (firstBodyElement != null) {
327           tryStatement.getParent().addRangeAfter(firstBodyElement, tryBlock.getLastBodyElement(), tryStatement);
328         }
329         tryStatement.delete();
330       }
331     }
332   }
333
334   private static void addExceptions(PsiClassType[] exceptionsToAdd, PsiTryStatement tryStatement) throws IncorrectOperationException {
335     for (PsiClassType type : exceptionsToAdd) {
336       final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(tryStatement.getProject());
337       String name = styleManager.suggestVariableName(VariableKind.PARAMETER, null, null, type).names[0];
338       name = styleManager.suggestUniqueVariableName(name, tryStatement, false);
339
340       PsiCatchSection catchSection =
341         JavaPsiFacade.getInstance(tryStatement.getProject()).getElementFactory().createCatchSection(type, name, tryStatement);
342       tryStatement.add(catchSection);
343     }
344   }
345
346   private static PsiClassType[] filterUnhandledExceptions(PsiClassType[] exceptions, PsiElement place) {
347     List<PsiClassType> result = new ArrayList<PsiClassType>();
348     for (PsiClassType exception : exceptions) {
349       if (!ExceptionUtil.isHandled(exception, place)) result.add(exception);
350     }
351     return result.toArray(new PsiClassType[result.size()]);
352   }
353
354   private static boolean isCatchParameterRedundant(PsiClassType catchParamType, Collection<PsiClassType> thrownTypes) {
355     for (PsiType exceptionType : thrownTypes) {
356       if (exceptionType.isConvertibleFrom(catchParamType)) return false;
357     }
358     return true;
359   }
360
361   //This methods works equally well for primary usages as well as for propagated callers' usages
362   private static void fixActualArgumentsList(PsiExpressionList list,
363                                              JavaChangeInfo changeInfo,
364                                              boolean toInsertDefaultValue, PsiSubstitutor substitutor) throws IncorrectOperationException {
365     final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory();
366     if (changeInfo.isParameterSetOrOrderChanged()) {
367       if (changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).isPropagationEnabled) {
368         final ParameterInfoImpl[] createdParmsInfo = ((JavaChangeInfoImpl)changeInfo).getCreatedParmsInfoWithoutVarargs();
369         for (ParameterInfoImpl info : createdParmsInfo) {
370           PsiExpression newArg;
371           if (toInsertDefaultValue) {
372             newArg = createDefaultValue(changeInfo, factory, info, list);
373           }
374           else {
375             newArg = factory.createExpressionFromText(info.getName(), list);
376           }
377           list.add(newArg);
378         }
379       }
380       else {
381         final PsiExpression[] args = list.getExpressions();
382         final int nonVarargCount = getNonVarargCount(changeInfo, args);
383         final int varargCount = args.length - nonVarargCount;
384         PsiExpression[] newVarargInitializers = null;
385
386         final int newArgsLength;
387         final int newNonVarargCount;
388         final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
389         if (changeInfo.isArrayToVarargs()) {
390           newNonVarargCount = newParms.length - 1;
391           final JavaParameterInfo lastNewParm = newParms[newParms.length - 1];
392           final PsiExpression arrayToConvert = args[lastNewParm.getOldIndex()];
393           if (arrayToConvert instanceof PsiNewExpression) {
394             final PsiNewExpression expression = (PsiNewExpression)arrayToConvert;
395             final PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
396             if (arrayInitializer != null) {
397               newVarargInitializers = arrayInitializer.getInitializers();
398             }
399           }
400           newArgsLength = newVarargInitializers == null ? newParms.length : newNonVarargCount + newVarargInitializers.length;
401         }
402         else if (changeInfo.isRetainsVarargs()) {
403           newNonVarargCount = newParms.length - 1;
404           newArgsLength = newNonVarargCount + varargCount;
405         }
406         else if (changeInfo.isObtainsVarags()) {
407           newNonVarargCount = newParms.length - 1;
408           newArgsLength = newNonVarargCount;
409         }
410         else {
411           newNonVarargCount = newParms.length;
412           newArgsLength = newParms.length;
413         }
414
415         String[] oldVarargs = null;
416         if (changeInfo.wasVararg() && !changeInfo.isRetainsVarargs()) {
417           oldVarargs = new String[varargCount];
418           for (int i = nonVarargCount; i < args.length; i++) {
419             oldVarargs[i - nonVarargCount] = args[i].getText();
420           }
421         }
422
423         final PsiExpression[] newArgs = new PsiExpression[newArgsLength];
424         for (int i = 0; i < newNonVarargCount; i++) {
425           if (newParms[i].getOldIndex() == nonVarargCount && oldVarargs != null) {
426             PsiType type = newParms[i].createType(changeInfo.getMethod(), list.getManager());
427             if (type instanceof PsiArrayType) {
428               type = substitutor.substitute(type);
429               type = TypeConversionUtil.erasure(type);
430               String typeText = type.getCanonicalText();
431               if (type instanceof PsiEllipsisType) {
432                 typeText = typeText.replace("...", "[]");
433               }
434               String text = "new " + typeText + "{" + StringUtil.join(oldVarargs, ",") + "}";
435               newArgs[i] = factory.createExpressionFromText(text, changeInfo.getMethod());
436               continue;
437             }
438           }
439           newArgs[i] = createActualArgument(changeInfo, list, newParms[i], toInsertDefaultValue, args);
440         }
441         if (changeInfo.isArrayToVarargs()) {
442           if (newVarargInitializers == null) {
443             newArgs[newNonVarargCount] =
444               createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args);
445           }
446           else {
447             System.arraycopy(newVarargInitializers, 0, newArgs, newNonVarargCount, newVarargInitializers.length);
448           }
449         }
450         else {
451           final int newVarargCount = newArgsLength - newNonVarargCount;
452           LOG.assertTrue(newVarargCount == 0 || newVarargCount == varargCount);
453           System.arraycopy(args, nonVarargCount, newArgs, newNonVarargCount, newVarargCount);
454         }
455         ChangeSignatureUtil.synchronizeList(list, Arrays.asList(newArgs), ExpressionList.INSTANCE, changeInfo.toRemoveParm());
456       }
457     }
458   }
459
460   private static int getNonVarargCount(JavaChangeInfo changeInfo, PsiExpression[] args) {
461     if (!changeInfo.wasVararg()) return args.length;
462     return changeInfo.getOldParameterTypes().length - 1;
463   }
464
465
466   @Nullable
467   private static PsiExpression createActualArgument(JavaChangeInfo changeInfo,
468                                                     final PsiExpressionList list,
469                                                     final JavaParameterInfo info,
470                                                     final boolean toInsertDefaultValue,
471                                                     final PsiExpression[] args) throws IncorrectOperationException {
472     final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory();
473     final int index = info.getOldIndex();
474     if (index >= 0) {
475       return args[index];
476     }
477     else {
478       if (toInsertDefaultValue) {
479         return createDefaultValue(changeInfo, factory, info, list);
480       }
481       else {
482         return factory.createExpressionFromText(info.getName(), list);
483       }
484     }
485   }
486
487   @Nullable
488   private static PsiExpression createDefaultValue(JavaChangeInfo changeInfo,
489                                                   final PsiElementFactory factory,
490                                                   final JavaParameterInfo info,
491                                                   final PsiExpressionList list)
492     throws IncorrectOperationException {
493     if (info.isUseAnySingleVariable()) {
494       final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper();
495       final PsiType type = info.getTypeWrapper().getType(changeInfo.getMethod(), list.getManager());
496       final VariablesProcessor processor = new VariablesProcessor(false) {
497         protected boolean check(PsiVariable var, ResolveState state) {
498           if (var instanceof PsiField && !resolveHelper.isAccessible((PsiField)var, list, null)) return false;
499           final PsiType varType = state.get(PsiSubstitutor.KEY).substitute(var.getType());
500           return type.isAssignableFrom(varType);
501         }
502
503         public boolean execute(PsiElement pe, ResolveState state) {
504           super.execute(pe, state);
505           return size() < 2;
506         }
507       };
508       PsiScopesUtil.treeWalkUp(processor, list, null);
509       if (processor.size() == 1) {
510         final PsiVariable result = processor.getResult(0);
511         return factory.createExpressionFromText(result.getName(), list);
512       }
513     }
514     final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(list, PsiCallExpression.class);
515     final String defaultValue = info.getDefaultValue();
516     return callExpression != null ? info.getValue(callExpression) : defaultValue.length() > 0 ? factory.createExpressionFromText(defaultValue, list) : null;
517   }
518
519
520   public boolean processPrimaryMethod(ChangeInfo changeInfo) {
521     if (!StdLanguages.JAVA.equals(changeInfo.getLanguage()) || !(changeInfo instanceof JavaChangeInfo)) return false;
522     final PsiElement element = changeInfo.getMethod();
523     LOG.assertTrue(element instanceof PsiMethod);
524     if (changeInfo.isGenerateDelegate()) {
525       generateDelegate((JavaChangeInfo)changeInfo);
526     }
527     processPrimaryMethod((JavaChangeInfo)changeInfo, (PsiMethod)element, null, true);
528     return true;
529   }
530
531   public boolean shouldPreviewUsages(ChangeInfo changeInfo, UsageInfo[] usages) {
532     return false;
533   }
534
535   @Override
536   public void setupDefaultValues(ChangeInfo changeInfo, Ref<UsageInfo[]> refUsages, Project project) {
537     if (!(changeInfo instanceof JavaChangeInfo)) return;
538     for (UsageInfo usageInfo : refUsages.get()) {
539       if (usageInfo instanceof  MethodCallUsageInfo) {
540         MethodCallUsageInfo methodCallUsageInfo = (MethodCallUsageInfo)usageInfo;
541         if (methodCallUsageInfo.isToChangeArguments()){
542           final PsiElement element = methodCallUsageInfo.getElement();
543           if (element == null) continue;
544           final boolean needDefaultValue = needDefaultValue(changeInfo, RefactoringUtil.getEnclosingMethod(element));
545           if (needDefaultValue) {
546             final ParameterInfo[] parameters = changeInfo.getNewParameters();
547             for (ParameterInfo parameter : parameters) {
548               final String defaultValue = parameter.getDefaultValue();
549               if (defaultValue == null && parameter.getOldIndex() == -1) {
550                 ((ParameterInfoImpl)parameter).setDefaultValue("");
551                 if (!ApplicationManager.getApplication().isUnitTestMode()) {
552                   final PsiType type = ((ParameterInfoImpl)parameter).getTypeWrapper().getType(element, element.getManager());
553                   final DefaultValueChooser chooser = new DefaultValueChooser(project, parameter.getName(), PsiTypesUtil.getDefaultValueOfType(type));
554                   chooser.show();
555                   if (chooser.isOK()) {
556                     if (chooser.feelLucky()) {
557                       parameter.setUseAnySingleVariable(true);
558                     } else {
559                       ((ParameterInfoImpl)parameter).setDefaultValue(chooser.getDefaultValue());
560                     }
561                   }
562                 }
563               }
564             }
565           }
566         }
567       }
568     }
569   }
570
571   private static boolean needDefaultValue(ChangeInfo changeInfo, PsiMethod method) {
572     return !(changeInfo instanceof JavaChangeInfoImpl) ||
573            !((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.contains(method);
574   }
575
576   private static void generateDelegate(JavaChangeInfo changeInfo) throws IncorrectOperationException {
577     final PsiMethod delegate = (PsiMethod)changeInfo.getMethod().copy();
578     final PsiClass targetClass = changeInfo.getMethod().getContainingClass();
579     LOG.assertTrue(!targetClass.isInterface());
580     PsiElementFactory factory = JavaPsiFacade.getElementFactory(targetClass.getProject());
581     ChangeSignatureProcessor.makeEmptyBody(factory, delegate);
582     final PsiCallExpression callExpression = ChangeSignatureProcessor.addDelegatingCallTemplate(delegate, changeInfo.getNewName());
583     addDelegateArguments(changeInfo, factory, callExpression);
584     targetClass.addBefore(delegate, changeInfo.getMethod());
585   }
586
587
588   private static void addDelegateArguments(JavaChangeInfo changeInfo, PsiElementFactory factory, final PsiCallExpression callExpression) throws IncorrectOperationException {
589     final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
590     final String[] oldParameterNames = changeInfo.getOldParameterNames();
591     for (int i = 0; i < newParms.length; i++) {
592       JavaParameterInfo newParm = newParms[i];
593       final PsiExpression actualArg;
594       if (newParm.getOldIndex() >= 0) {
595         actualArg = factory.createExpressionFromText(oldParameterNames[newParm.getOldIndex()], callExpression);
596       }
597       else {
598         actualArg = changeInfo.getValue(i, callExpression);
599       }
600       callExpression.getArgumentList().add(actualArg);
601     }
602   }
603
604   private static void processPrimaryMethod(JavaChangeInfo changeInfo, PsiMethod method,
605                                            PsiMethod baseMethod,
606                                            boolean isOriginal) throws IncorrectOperationException {
607     PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
608
609     if (changeInfo.isVisibilityChanged()) {
610       PsiModifierList modifierList = method.getModifierList();
611       final String highestVisibility = isOriginal
612                                        ? changeInfo.getNewVisibility()
613                                        : VisibilityUtil.getHighestVisibility(changeInfo.getNewVisibility(),
614                                                                              VisibilityUtil.getVisibilityModifier(modifierList));
615       VisibilityUtil.setVisibility(modifierList, highestVisibility);
616     }
617
618     if (changeInfo.isNameChanged()) {
619       String newName = baseMethod == null ? changeInfo.getNewName() :
620                        RefactoringUtil.suggestNewOverriderName(method.getName(), baseMethod.getName(), changeInfo.getNewName());
621
622       if (newName != null && !newName.equals(method.getName())) {
623         final PsiIdentifier nameId = method.getNameIdentifier();
624         assert nameId != null;
625         nameId.replace(JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createIdentifier(newName));
626       }
627     }
628
629     final PsiSubstitutor substitutor =
630       baseMethod == null ? PsiSubstitutor.EMPTY : ChangeSignatureProcessor.calculateSubstitutor(method, baseMethod);
631
632     if (changeInfo.isReturnTypeChanged()) {
633       PsiType newTypeElement = changeInfo.getNewReturnType().getType(changeInfo.getMethod().getParameterList(), method.getManager());
634       final PsiType returnType = substitutor.substitute(newTypeElement);
635       // don't modify return type for non-Java overriders (EJB)
636       if (method.getName().equals(changeInfo.getNewName())) {
637         final PsiTypeElement typeElement = method.getReturnTypeElement();
638         if (typeElement != null) {
639           typeElement.replace(factory.createTypeElement(returnType));
640         }
641       }
642     }
643
644     PsiParameterList list = method.getParameterList();
645     PsiParameter[] parameters = list.getParameters();
646
647     final JavaParameterInfo[] parameterInfos = changeInfo.getNewParameters();
648     PsiParameter[] newParms = new PsiParameter[parameterInfos.length -
649                                                (baseMethod != null ? baseMethod.getParameterList().getParametersCount() -
650                                                                      method.getParameterList().getParametersCount() : 0)];
651     final String[] oldParameterNames = changeInfo.getOldParameterNames();
652     final String[] oldParameterTypes = changeInfo.getOldParameterTypes();
653     for (int i = 0; i < newParms.length; i++) {
654       JavaParameterInfo info = parameterInfos[i];
655       int index = info.getOldIndex();
656       if (index >= 0) {
657         PsiParameter parameter = parameters[index];
658         newParms[i] = parameter;
659
660         String oldName = oldParameterNames[index];
661         if (!oldName.equals(info.getName()) && oldName.equals(parameter.getName())) {
662           PsiIdentifier newIdentifier = factory.createIdentifier(info.getName());
663           parameter.getNameIdentifier().replace(newIdentifier);
664         }
665
666         String oldType = oldParameterTypes[index];
667         if (!oldType.equals(info.getTypeText())) {
668           parameter.normalizeDeclaration();
669           PsiType newType = substitutor.substitute(info.createType(changeInfo.getMethod().getParameterList(), method.getManager()));
670
671           parameter.getTypeElement().replace(factory.createTypeElement(newType));
672         }
673       }
674       else {
675         newParms[i] = createNewParameter(changeInfo, info, substitutor);
676       }
677     }
678
679
680     resolveParameterVsFieldsConflicts(newParms, method, list, changeInfo.toRemoveParm());
681     fixJavadocsForChangedMethod(method, changeInfo, newParms.length);
682     if (changeInfo.isExceptionSetOrOrderChanged()) {
683       final PsiClassType[] newExceptions = getPrimaryChangedExceptionInfo(changeInfo);
684       fixPrimaryThrowsLists(method, newExceptions);
685     }
686   }
687
688   private static PsiClassType[] getPrimaryChangedExceptionInfo(JavaChangeInfo changeInfo) throws IncorrectOperationException {
689     final ThrownExceptionInfo[] newExceptionInfos = changeInfo.getNewExceptions();
690     PsiClassType[] newExceptions = new PsiClassType[newExceptionInfos.length];
691     final PsiMethod method = changeInfo.getMethod();
692     for (int i = 0; i < newExceptions.length; i++) {
693       newExceptions[i] =
694         (PsiClassType)newExceptionInfos[i].createType(method, method.getManager()); //context really does not matter here
695     }
696     return newExceptions;
697   }
698
699
700   private static void processCallerMethod(JavaChangeInfo changeInfo, PsiMethod caller,
701                                           PsiMethod baseMethod,
702                                           boolean toInsertParams,
703                                           boolean toInsertThrows) throws IncorrectOperationException {
704     LOG.assertTrue(toInsertParams || toInsertThrows);
705     if (toInsertParams) {
706       List<PsiParameter> newParameters = new ArrayList<PsiParameter>();
707       ContainerUtil.addAll(newParameters, caller.getParameterList().getParameters());
708       final JavaParameterInfo[] primaryNewParms = changeInfo.getNewParameters();
709       PsiSubstitutor substitutor =
710         baseMethod == null ? PsiSubstitutor.EMPTY : ChangeSignatureProcessor.calculateSubstitutor(caller, baseMethod);
711       for (JavaParameterInfo info : primaryNewParms) {
712         if (info.getOldIndex() < 0) newParameters.add(createNewParameter(changeInfo, info, substitutor));
713       }
714       PsiParameter[] arrayed = newParameters.toArray(new PsiParameter[newParameters.size()]);
715       boolean[] toRemoveParm = new boolean[arrayed.length];
716       Arrays.fill(toRemoveParm, false);
717       resolveParameterVsFieldsConflicts(arrayed, caller, caller.getParameterList(), toRemoveParm);
718     }
719
720     if (toInsertThrows) {
721       List<PsiJavaCodeReferenceElement> newThrowns = new ArrayList<PsiJavaCodeReferenceElement>();
722       final PsiReferenceList throwsList = caller.getThrowsList();
723       ContainerUtil.addAll(newThrowns, throwsList.getReferenceElements());
724       final ThrownExceptionInfo[] primaryNewExns = changeInfo.getNewExceptions();
725       for (ThrownExceptionInfo thrownExceptionInfo : primaryNewExns) {
726         if (thrownExceptionInfo.getOldIndex() < 0) {
727           final PsiClassType type = (PsiClassType)thrownExceptionInfo.createType(caller, caller.getManager());
728           final PsiJavaCodeReferenceElement ref =
729             JavaPsiFacade.getInstance(caller.getProject()).getElementFactory().createReferenceElementByType(type);
730           newThrowns.add(ref);
731         }
732       }
733       PsiJavaCodeReferenceElement[] arrayed = newThrowns.toArray(new PsiJavaCodeReferenceElement[newThrowns.size()]);
734       boolean[] toRemoveParm = new boolean[arrayed.length];
735       Arrays.fill(toRemoveParm, false);
736       ChangeSignatureUtil.synchronizeList(throwsList, Arrays.asList(arrayed), ThrowsList.INSTANCE, toRemoveParm);
737     }
738   }
739
740   private static void fixPrimaryThrowsLists(PsiMethod method, PsiClassType[] newExceptions) throws IncorrectOperationException {
741     PsiElementFactory elementFactory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
742     PsiJavaCodeReferenceElement[] refs = new PsiJavaCodeReferenceElement[newExceptions.length];
743     for (int i = 0; i < refs.length; i++) {
744       refs[i] = elementFactory.createReferenceElementByType(newExceptions[i]);
745     }
746     PsiReferenceList throwsList = elementFactory.createReferenceList(refs);
747
748     PsiReferenceList methodThrowsList = (PsiReferenceList)method.getThrowsList().replace(throwsList);
749     methodThrowsList = (PsiReferenceList)JavaCodeStyleManager.getInstance(method.getProject()).shortenClassReferences(methodThrowsList);
750     method.getManager().getCodeStyleManager().reformatRange(method, method.getParameterList().getTextRange().getEndOffset(),
751                                                             methodThrowsList.getTextRange().getEndOffset());
752   }
753
754   private static void fixJavadocsForChangedMethod(PsiMethod method, JavaChangeInfo changeInfo, int newParamsLength) throws IncorrectOperationException {
755     final PsiParameter[] parameters = method.getParameterList().getParameters();
756     final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
757     LOG.assertTrue(parameters.length <= newParamsLength);
758     final Set<PsiParameter> newParameters = new HashSet<PsiParameter>();
759     final String[] oldParameterNames = changeInfo.getOldParameterNames();
760     for (int i = 0; i < newParamsLength; i++) {
761       JavaParameterInfo newParm = newParms[i];
762       if (newParm.getOldIndex() < 0 ||
763           !newParm.getName().equals(oldParameterNames[newParm.getOldIndex()])) {
764         newParameters.add(parameters[i]);
765       }
766     }
767     RefactoringUtil.fixJavadocsForParams(method, newParameters);
768   }
769
770   private static PsiParameter createNewParameter(JavaChangeInfo changeInfo, JavaParameterInfo newParm,
771                                                  PsiSubstitutor substitutor) throws IncorrectOperationException {
772     final PsiParameterList list = changeInfo.getMethod().getParameterList();
773     final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory();
774     final PsiType type = substitutor.substitute(newParm.createType(list, list.getManager()));
775     return factory.createParameter(newParm.getName(), type);
776   }
777
778   private static void resolveParameterVsFieldsConflicts(final PsiParameter[] newParms,
779                                                         final PsiMethod method,
780                                                         final PsiParameterList list,
781                                                         boolean[] toRemoveParm) throws IncorrectOperationException {
782     List<FieldConflictsResolver> conflictResolvers = new ArrayList<FieldConflictsResolver>();
783     for (PsiParameter parameter : newParms) {
784       conflictResolvers.add(new FieldConflictsResolver(parameter.getName(), method.getBody()));
785     }
786     ChangeSignatureUtil.synchronizeList(list, Arrays.asList(newParms), ParameterList.INSTANCE, toRemoveParm);
787     JavaCodeStyleManager.getInstance(list.getProject()).shortenClassReferences(list);
788     for (FieldConflictsResolver fieldConflictsResolver : conflictResolvers) {
789       fieldConflictsResolver.fix();
790     }
791   }
792
793   private static boolean needToCatchExceptions(JavaChangeInfo changeInfo, PsiMethod caller) {
794     return changeInfo.isExceptionSetOrOrderChanged() &&
795            !(changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).propagateExceptionsMethods.contains(caller));
796   }
797
798   private static class ParameterList implements ChangeSignatureUtil.ChildrenGenerator<PsiParameterList, PsiParameter> {
799     public static final ParameterList INSTANCE = new ParameterList();
800
801     public List<PsiParameter> getChildren(PsiParameterList psiParameterList) {
802       return Arrays.asList(psiParameterList.getParameters());
803     }
804   }
805
806   private static class ThrowsList implements ChangeSignatureUtil.ChildrenGenerator<PsiReferenceList, PsiJavaCodeReferenceElement> {
807     public static final ThrowsList INSTANCE = new ThrowsList();
808
809     public List<PsiJavaCodeReferenceElement> getChildren(PsiReferenceList throwsList) {
810       return Arrays.asList(throwsList.getReferenceElements());
811     }
812   }
813
814   private static class ConflictSearcher {
815     private final JavaChangeInfo myChangeInfo;
816
817     private ConflictSearcher(JavaChangeInfo changeInfo) {
818       this.myChangeInfo = changeInfo;
819     }
820
821     public MultiMap<PsiElement, String> findConflicts(Ref<UsageInfo[]> refUsages) {
822       MultiMap<PsiElement, String> conflictDescriptions = new MultiMap<PsiElement, String>();
823       addMethodConflicts(conflictDescriptions);
824       Set<UsageInfo> usagesSet = new HashSet<UsageInfo>(Arrays.asList(refUsages.get()));
825       RenameUtil.removeConflictUsages(usagesSet);
826       if (myChangeInfo.isVisibilityChanged()) {
827         try {
828           addInaccessibilityDescriptions(usagesSet, conflictDescriptions);
829         }
830         catch (IncorrectOperationException e) {
831           LOG.error(e);
832         }
833       }
834
835       return conflictDescriptions;
836     }
837
838     private boolean needToChangeCalls() {
839       return myChangeInfo.isNameChanged() || myChangeInfo.isParameterSetOrOrderChanged() || myChangeInfo.isExceptionSetOrOrderChanged();
840     }
841
842
843     private void addInaccessibilityDescriptions(Set<UsageInfo> usages, MultiMap<PsiElement, String> conflictDescriptions)
844       throws IncorrectOperationException {
845       PsiMethod method = myChangeInfo.getMethod();
846       PsiModifierList modifierList = (PsiModifierList)method.getModifierList().copy();
847       VisibilityUtil.setVisibility(modifierList, myChangeInfo.getNewVisibility());
848
849       for (Iterator<UsageInfo> iterator = usages.iterator(); iterator.hasNext();) {
850         UsageInfo usageInfo = iterator.next();
851         PsiElement element = usageInfo.getElement();
852         if (element != null && StdLanguages.JAVA.equals(element.getLanguage())) {
853           if (element instanceof PsiReferenceExpression) {
854             PsiClass accessObjectClass = null;
855             PsiExpression qualifier = ((PsiReferenceExpression)element).getQualifierExpression();
856             if (qualifier != null) {
857               accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
858             }
859
860             if (!JavaPsiFacade.getInstance(element.getProject()).getResolveHelper()
861               .isAccessible(method, modifierList, element, accessObjectClass, null)) {
862               String message =
863                 RefactoringBundle.message("0.with.1.visibility.is.not.accessible.from.2",
864                                           RefactoringUIUtil.getDescription(method, true),
865                                           myChangeInfo.getNewVisibility(),
866                                           RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(element), true));
867               conflictDescriptions.putValue(method, message);
868               if (!needToChangeCalls()) {
869                 iterator.remove();
870               }
871             }
872           }
873         }
874       }
875     }
876
877
878     private void addMethodConflicts(MultiMap<PsiElement, String> conflicts) {
879       String newMethodName = myChangeInfo.getNewName();
880       if (!(myChangeInfo instanceof JavaChangeInfo)) {
881         return;
882       }
883       try {
884         PsiMethod prototype;
885         final PsiMethod method = myChangeInfo.getMethod();
886         if (!StdLanguages.JAVA.equals(method.getLanguage())) return;
887         PsiManager manager = PsiManager.getInstance(method.getProject());
888         PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
889         final CanonicalTypes.Type returnType = myChangeInfo.getNewReturnType();
890         if (returnType != null) {
891           prototype = factory.createMethod(newMethodName, returnType.getType(method, manager));
892         }
893         else {
894           prototype = factory.createConstructor();
895           prototype.setName(newMethodName);
896         }
897         JavaParameterInfo[] parameters = myChangeInfo.getNewParameters();
898
899
900         for (JavaParameterInfo info : parameters) {
901           PsiType parameterType = info.createType(method, manager);
902           if (parameterType == null) {
903             parameterType =
904               JavaPsiFacade.getElementFactory(method.getProject()).createTypeFromText(CommonClassNames.JAVA_LANG_OBJECT, method);
905           }
906           PsiParameter param = factory.createParameter(info.getName(), parameterType);
907           prototype.getParameterList().add(param);
908         }
909
910         ConflictsUtil.checkMethodConflicts(method.getContainingClass(), method, prototype, conflicts);
911       }
912       catch (IncorrectOperationException e) {
913         LOG.error(e);
914       }
915     }
916   }
917
918   private static class ExpressionList implements ChangeSignatureUtil.ChildrenGenerator<PsiExpressionList, PsiExpression> {
919     public static final ExpressionList INSTANCE = new ExpressionList();
920
921     public List<PsiExpression> getChildren(PsiExpressionList psiExpressionList) {
922       return Arrays.asList(psiExpressionList.getExpressions());
923     }
924   }
925
926 }