2 * Copyright 2000-2010 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.
16 package com.intellij.refactoring.changeSignature;
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;
48 * @author Maxim.Medvedev
50 public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
51 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeSignature.JavaChangeSignatureUsageProcessor");
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;
59 public UsageInfo[] findUsages(ChangeInfo info) {
60 if (info instanceof JavaChangeInfo) {
61 return new JavaChangeSignatureUsageSearcher((JavaChangeInfo)info).findUsages();
64 return UsageInfo.EMPTY_ARRAY;
68 public MultiMap<PsiElement, String> findConflicts(ChangeInfo info, Ref<UsageInfo[]> refUsages) {
69 if (info instanceof JavaChangeInfo) {
70 return new ConflictSearcher((JavaChangeInfo)info).findConflicts(refUsages);
73 return new MultiMap<PsiElement, String>();
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;
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());
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);
97 processCallerMethod((JavaChangeInfo)changeInfo, method, baseMethod, info.isToInsertArgs(), info.isToCatchExceptions());
104 PsiElement element = usage.getElement();
105 LOG.assertTrue(element != null);
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);
117 ((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.add(constructor);
120 addSuperCall((JavaChangeInfo)changeInfo, constructor, defConstructorUsage.getBaseConstructor(), usages);
123 else if (usage instanceof NoConstructorClassUsageInfo) {
124 addDefaultConstructor(((JavaChangeInfo)changeInfo), ((NoConstructorClassUsageInfo)usage).getPsiClass(), usages);
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);
133 else if (usage instanceof ChangeSignatureParameterUsageInfo) {
134 String newName = ((ChangeSignatureParameterUsageInfo)usage).newParameterName;
135 String oldName = ((ChangeSignatureParameterUsageInfo)usage).oldParameterName;
136 processParameterUsage((PsiReferenceExpression)element, oldName, newName);
139 else if (usage instanceof CallReferenceUsageInfo) {
140 ((CallReferenceUsageInfo)usage).getReference().handleChangeSignature(changeInfo);
143 else if (element instanceof PsiEnumConstant) {
144 fixActualArgumentsList(((PsiEnumConstant)element).getArgumentList(), (JavaChangeInfo)changeInfo, true, PsiSubstitutor.EMPTY);
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);
160 private static void processParameterUsage(PsiReferenceExpression ref, String oldName, String newName)
161 throws IncorrectOperationException {
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);
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);
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);
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();
199 PsiStatement[] statements = body.getStatements();
200 if (statements.length > 0) {
201 superCall = (PsiExpressionStatement)body.addBefore(superCall, statements[0]);
204 superCall = (PsiExpressionStatement)body.add(superCall);
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);
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());
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;
238 fixActualArgumentsList(list, changeInfo, toInsertDefaultValue, subsitutor);
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);
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)) {
263 private static PsiClassType[] getCalleeChangedExceptionInfo(final PsiMethod callee) {
264 return callee.getThrowsList().getReferencedTypes(); //Callee method's throws list is already modified!
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);
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();
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();
282 if (!(caughtType instanceof PsiClassType)) continue;
283 if (ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)caughtType)) continue;
285 if (!isCatchParameterRedundant((PsiClassType)caughtType, classes)) continue;
286 parameter.getParent().delete(); //delete catch section
289 PsiClassType[] exceptionsToAdd = filterUnhandledExceptions(newExceptions, tryBlock);
290 addExceptions(exceptionsToAdd, tryStatement);
292 adjustPossibleEmptyTryStatement(tryStatement);
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);
305 addExceptions(newExceptions, tryStatement);
307 tryStatement.getCatchSections()[0].delete(); //Delete dummy catch section
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);
317 return result.toArray(new PsiClassType[result.size()]);
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);
329 tryStatement.delete();
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);
340 PsiCatchSection catchSection =
341 JavaPsiFacade.getInstance(tryStatement.getProject()).getElementFactory().createCatchSection(type, name, tryStatement);
342 tryStatement.add(catchSection);
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);
351 return result.toArray(new PsiClassType[result.size()]);
354 private static boolean isCatchParameterRedundant(PsiClassType catchParamType, Collection<PsiClassType> thrownTypes) {
355 for (PsiType exceptionType : thrownTypes) {
356 if (exceptionType.isConvertibleFrom(catchParamType)) return false;
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);
375 newArg = factory.createExpressionFromText(info.getName(), list);
381 final PsiExpression[] args = list.getExpressions();
382 final int nonVarargCount = getNonVarargCount(changeInfo, args);
383 final int varargCount = args.length - nonVarargCount;
384 PsiExpression[] newVarargInitializers = null;
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();
400 newArgsLength = newVarargInitializers == null ? newParms.length : newNonVarargCount + newVarargInitializers.length;
402 else if (changeInfo.isRetainsVarargs()) {
403 newNonVarargCount = newParms.length - 1;
404 newArgsLength = newNonVarargCount + varargCount;
406 else if (changeInfo.isObtainsVarags()) {
407 newNonVarargCount = newParms.length - 1;
408 newArgsLength = newNonVarargCount;
411 newNonVarargCount = newParms.length;
412 newArgsLength = newParms.length;
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();
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("...", "[]");
434 String text = "new " + typeText + "{" + StringUtil.join(oldVarargs, ",") + "}";
435 newArgs[i] = factory.createExpressionFromText(text, changeInfo.getMethod());
439 newArgs[i] = createActualArgument(changeInfo, list, newParms[i], toInsertDefaultValue, args);
441 if (changeInfo.isArrayToVarargs()) {
442 if (newVarargInitializers == null) {
443 newArgs[newNonVarargCount] =
444 createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args);
447 System.arraycopy(newVarargInitializers, 0, newArgs, newNonVarargCount, newVarargInitializers.length);
451 final int newVarargCount = newArgsLength - newNonVarargCount;
452 LOG.assertTrue(newVarargCount == 0 || newVarargCount == varargCount);
453 System.arraycopy(args, nonVarargCount, newArgs, newNonVarargCount, newVarargCount);
455 ChangeSignatureUtil.synchronizeList(list, Arrays.asList(newArgs), ExpressionList.INSTANCE, changeInfo.toRemoveParm());
460 private static int getNonVarargCount(JavaChangeInfo changeInfo, PsiExpression[] args) {
461 if (!changeInfo.wasVararg()) return args.length;
462 return changeInfo.getOldParameterTypes().length - 1;
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();
478 if (toInsertDefaultValue) {
479 return createDefaultValue(changeInfo, factory, info, list);
482 return factory.createExpressionFromText(info.getName(), list);
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);
503 public boolean execute(PsiElement pe, ResolveState state) {
504 super.execute(pe, state);
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);
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;
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);
527 processPrimaryMethod((JavaChangeInfo)changeInfo, (PsiMethod)element, null, true);
531 public boolean shouldPreviewUsages(ChangeInfo changeInfo, UsageInfo[] usages) {
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));
555 if (chooser.isOK()) {
556 if (chooser.feelLucky()) {
557 parameter.setUseAnySingleVariable(true);
559 ((ParameterInfoImpl)parameter).setDefaultValue(chooser.getDefaultValue());
571 private static boolean needDefaultValue(ChangeInfo changeInfo, PsiMethod method) {
572 return !(changeInfo instanceof JavaChangeInfoImpl) ||
573 !((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.contains(method);
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());
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);
598 actualArg = changeInfo.getValue(i, callExpression);
600 callExpression.getArgumentList().add(actualArg);
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();
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);
618 if (changeInfo.isNameChanged()) {
619 String newName = baseMethod == null ? changeInfo.getNewName() :
620 RefactoringUtil.suggestNewOverriderName(method.getName(), baseMethod.getName(), changeInfo.getNewName());
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));
629 final PsiSubstitutor substitutor =
630 baseMethod == null ? PsiSubstitutor.EMPTY : ChangeSignatureProcessor.calculateSubstitutor(method, baseMethod);
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));
644 PsiParameterList list = method.getParameterList();
645 PsiParameter[] parameters = list.getParameters();
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();
657 PsiParameter parameter = parameters[index];
658 newParms[i] = parameter;
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);
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()));
671 parameter.getTypeElement().replace(factory.createTypeElement(newType));
675 newParms[i] = createNewParameter(changeInfo, info, substitutor);
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);
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++) {
694 (PsiClassType)newExceptionInfos[i].createType(method, method.getManager()); //context really does not matter here
696 return newExceptions;
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));
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);
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);
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);
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]);
746 PsiReferenceList throwsList = elementFactory.createReferenceList(refs);
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());
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]);
767 RefactoringUtil.fixJavadocsForParams(method, newParameters);
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);
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()));
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();
793 private static boolean needToCatchExceptions(JavaChangeInfo changeInfo, PsiMethod caller) {
794 return changeInfo.isExceptionSetOrOrderChanged() &&
795 !(changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).propagateExceptionsMethods.contains(caller));
798 private static class ParameterList implements ChangeSignatureUtil.ChildrenGenerator<PsiParameterList, PsiParameter> {
799 public static final ParameterList INSTANCE = new ParameterList();
801 public List<PsiParameter> getChildren(PsiParameterList psiParameterList) {
802 return Arrays.asList(psiParameterList.getParameters());
806 private static class ThrowsList implements ChangeSignatureUtil.ChildrenGenerator<PsiReferenceList, PsiJavaCodeReferenceElement> {
807 public static final ThrowsList INSTANCE = new ThrowsList();
809 public List<PsiJavaCodeReferenceElement> getChildren(PsiReferenceList throwsList) {
810 return Arrays.asList(throwsList.getReferenceElements());
814 private static class ConflictSearcher {
815 private final JavaChangeInfo myChangeInfo;
817 private ConflictSearcher(JavaChangeInfo changeInfo) {
818 this.myChangeInfo = changeInfo;
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()) {
828 addInaccessibilityDescriptions(usagesSet, conflictDescriptions);
830 catch (IncorrectOperationException e) {
835 return conflictDescriptions;
838 private boolean needToChangeCalls() {
839 return myChangeInfo.isNameChanged() || myChangeInfo.isParameterSetOrOrderChanged() || myChangeInfo.isExceptionSetOrOrderChanged();
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());
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();
860 if (!JavaPsiFacade.getInstance(element.getProject()).getResolveHelper()
861 .isAccessible(method, modifierList, element, accessObjectClass, null)) {
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()) {
878 private void addMethodConflicts(MultiMap<PsiElement, String> conflicts) {
879 String newMethodName = myChangeInfo.getNewName();
880 if (!(myChangeInfo instanceof JavaChangeInfo)) {
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));
894 prototype = factory.createConstructor();
895 prototype.setName(newMethodName);
897 JavaParameterInfo[] parameters = myChangeInfo.getNewParameters();
900 for (JavaParameterInfo info : parameters) {
901 PsiType parameterType = info.createType(method, manager);
902 if (parameterType == null) {
904 JavaPsiFacade.getElementFactory(method.getProject()).createTypeFromText(CommonClassNames.JAVA_LANG_OBJECT, method);
906 PsiParameter param = factory.createParameter(info.getName(), parameterType);
907 prototype.getParameterList().add(param);
910 ConflictsUtil.checkMethodConflicts(method.getContainingClass(), method, prototype, conflicts);
912 catch (IncorrectOperationException e) {
918 private static class ExpressionList implements ChangeSignatureUtil.ChildrenGenerator<PsiExpressionList, PsiExpression> {
919 public static final ExpressionList INSTANCE = new ExpressionList();
921 public List<PsiExpression> getChildren(PsiExpressionList psiExpressionList) {
922 return Arrays.asList(psiExpressionList.getExpressions());