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.diagnostic.Logger;
21 import com.intellij.openapi.util.Ref;
22 import com.intellij.openapi.util.text.StringUtil;
23 import com.intellij.psi.*;
24 import com.intellij.psi.codeStyle.CodeStyleManager;
25 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
26 import com.intellij.psi.codeStyle.VariableKind;
27 import com.intellij.psi.scope.processor.VariablesProcessor;
28 import com.intellij.psi.scope.util.PsiScopesUtil;
29 import com.intellij.psi.util.MethodSignatureUtil;
30 import com.intellij.psi.util.PsiTreeUtil;
31 import com.intellij.psi.util.PsiUtil;
32 import com.intellij.psi.util.TypeConversionUtil;
33 import com.intellij.refactoring.RefactoringBundle;
34 import com.intellij.refactoring.rename.RenameUtil;
35 import com.intellij.refactoring.util.*;
36 import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
37 import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo;
38 import com.intellij.usageView.UsageInfo;
39 import com.intellij.util.IncorrectOperationException;
40 import com.intellij.util.VisibilityUtil;
41 import com.intellij.util.containers.ContainerUtil;
42 import com.intellij.util.containers.HashSet;
43 import com.intellij.util.containers.MultiMap;
44 import org.jetbrains.annotations.Nullable;
49 * @author Maxim.Medvedev
51 public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
52 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeSignature.JavaChangeSignatureUsageProcessor");
54 private static boolean isJavaUsage(UsageInfo info) {
55 final PsiElement element = info.getElement();
56 if (element == null) return false;
57 return element.getLanguage() == StdLanguages.JAVA;
60 public UsageInfo[] findUsages(ChangeInfo info) {
61 if (info instanceof JavaChangeInfo) {
62 return new JavaChangeSignatureUsageSearcher((JavaChangeInfo)info).findUsages();
65 return UsageInfo.EMPTY_ARRAY;
69 public MultiMap<PsiElement, String> findConflicts(ChangeInfo info, Ref<UsageInfo[]> refUsages) {
70 if (info instanceof JavaChangeInfo) {
71 return new ConflictSearcher((JavaChangeInfo)info).findConflicts(refUsages);
74 return new MultiMap<PsiElement, String>();
78 public boolean processUsage(ChangeInfo changeInfo, UsageInfo usage, boolean beforeMethodChange, UsageInfo[] usages) {
79 if (!isJavaUsage(usage)) return false;
80 if (!(changeInfo instanceof JavaChangeInfo)) return false;
83 if (beforeMethodChange) {
84 if (usage instanceof CallerUsageInfo) {
85 final CallerUsageInfo callerUsageInfo = (CallerUsageInfo)usage;
86 processCallerMethod((JavaChangeInfo)changeInfo, callerUsageInfo.getMethod(), null, callerUsageInfo.isToInsertParameter(),
87 callerUsageInfo.isToInsertException());
90 else if (usage instanceof OverriderUsageInfo) {
91 OverriderUsageInfo info = (OverriderUsageInfo)usage;
92 final PsiMethod method = info.getElement();
93 final PsiMethod baseMethod = info.getBaseMethod();
94 if (info.isOriginalOverrider()) {
95 processPrimaryMethod((JavaChangeInfo)changeInfo, method, baseMethod, false);
98 processCallerMethod((JavaChangeInfo)changeInfo, method, baseMethod, info.isToInsertArgs(), info.isToCatchExceptions());
105 PsiElement element = usage.getElement();
106 LOG.assertTrue(element != null);
108 if (usage instanceof DefaultConstructorImplicitUsageInfo) {
109 final DefaultConstructorImplicitUsageInfo defConstructorUsage = (DefaultConstructorImplicitUsageInfo)usage;
110 PsiMethod constructor = defConstructorUsage.getConstructor();
111 if (!constructor.isPhysical()) {
112 final boolean toPropagate =
113 changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.remove(constructor);
114 final PsiClass containingClass = defConstructorUsage.getContainingClass();
115 constructor = (PsiMethod)containingClass.add(constructor);
116 PsiUtil.setModifierProperty(constructor, VisibilityUtil.getVisibilityModifier(containingClass.getModifierList()), true);
118 ((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.add(constructor);
121 addSuperCall((JavaChangeInfo)changeInfo, constructor, defConstructorUsage.getBaseConstructor(), usages);
124 else if (usage instanceof NoConstructorClassUsageInfo) {
125 addDefaultConstructor(((JavaChangeInfo)changeInfo), ((NoConstructorClassUsageInfo)usage).getPsiClass(), usages);
128 else if (usage instanceof MethodCallUsageInfo) {
129 final MethodCallUsageInfo methodCallInfo = (MethodCallUsageInfo)usage;
130 processMethodUsage(methodCallInfo.getElement(), (JavaChangeInfo)changeInfo, methodCallInfo.isToChangeArguments(),
131 methodCallInfo.isToCatchExceptions(), methodCallInfo.getReferencedMethod(), methodCallInfo.getSubstitutor(), usages);
134 else if (usage instanceof ChangeSignatureParameterUsageInfo) {
135 String newName = ((ChangeSignatureParameterUsageInfo)usage).newParameterName;
136 String oldName = ((ChangeSignatureParameterUsageInfo)usage).oldParameterName;
137 processParameterUsage((PsiReferenceExpression)element, oldName, newName);
140 else if (usage instanceof CallReferenceUsageInfo) {
141 ((CallReferenceUsageInfo)usage).getReference().handleChangeSignature(changeInfo);
144 else if (element instanceof PsiEnumConstant) {
145 fixActualArgumentsList(((PsiEnumConstant)element).getArgumentList(), (JavaChangeInfo)changeInfo, true, PsiSubstitutor.EMPTY);
148 else if (!(usage instanceof OverriderUsageInfo)) {
149 PsiReference reference = usage instanceof MoveRenameUsageInfo ? usage.getReference() : element.getReference();
150 if (reference != null) {
151 PsiElement target = changeInfo.getMethod();
152 if (target != null) {
153 reference.bindToElement(target);
161 private static void processParameterUsage(PsiReferenceExpression ref, String oldName, String newName)
162 throws IncorrectOperationException {
164 PsiElement last = ref.getReferenceNameElement();
165 if (last instanceof PsiIdentifier && last.getText().equals(oldName)) {
166 PsiElementFactory factory = JavaPsiFacade.getInstance(ref.getProject()).getElementFactory();
167 PsiIdentifier newNameIdentifier = factory.createIdentifier(newName);
168 last.replace(newNameIdentifier);
173 private static void addDefaultConstructor(JavaChangeInfo changeInfo, PsiClass aClass, final UsageInfo[] usages)
174 throws IncorrectOperationException {
175 if (!(aClass instanceof PsiAnonymousClass)) {
176 PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject());
177 PsiMethod defaultConstructor = factory.createMethodFromText(aClass.getName() + "(){}", aClass);
178 defaultConstructor = (PsiMethod)CodeStyleManager.getInstance(aClass.getProject()).reformat(defaultConstructor);
179 defaultConstructor = (PsiMethod)aClass.add(defaultConstructor);
180 PsiUtil.setModifierProperty(defaultConstructor, VisibilityUtil.getVisibilityModifier(aClass.getModifierList()), true);
181 addSuperCall(changeInfo, defaultConstructor, null, usages);
184 final PsiElement parent = aClass.getParent();
185 if (parent instanceof PsiNewExpression) {
186 final PsiExpressionList argumentList = ((PsiNewExpression)parent).getArgumentList();
187 final PsiClass baseClass = changeInfo.getMethod().getContainingClass();
188 final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, aClass, PsiSubstitutor.EMPTY);
189 fixActualArgumentsList(argumentList, changeInfo, true, substitutor);
194 private static void addSuperCall(JavaChangeInfo changeInfo, PsiMethod constructor, PsiMethod callee, final UsageInfo[] usages)
195 throws IncorrectOperationException {
196 final PsiElementFactory factory = JavaPsiFacade.getElementFactory(constructor.getProject());
197 PsiExpressionStatement superCall = (PsiExpressionStatement)factory.createStatementFromText("super();", constructor);
198 PsiCodeBlock body = constructor.getBody();
200 PsiStatement[] statements = body.getStatements();
201 if (statements.length > 0) {
202 superCall = (PsiExpressionStatement)body.addBefore(superCall, statements[0]);
205 superCall = (PsiExpressionStatement)body.add(superCall);
207 PsiMethodCallExpression callExpression = (PsiMethodCallExpression)superCall.getExpression();
208 final PsiClass aClass = constructor.getContainingClass();
209 final PsiClass baseClass = changeInfo.getMethod().getContainingClass();
210 final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, aClass, PsiSubstitutor.EMPTY);
211 processMethodUsage(callExpression.getMethodExpression(), changeInfo, true, false, callee, substitutor, usages);
214 private static void processMethodUsage(PsiElement ref,
215 JavaChangeInfo changeInfo,
216 boolean toChangeArguments,
217 boolean toCatchExceptions,
218 PsiMethod callee, PsiSubstitutor subsitutor, final UsageInfo[] usages) throws IncorrectOperationException {
219 if (changeInfo.isNameChanged()) {
220 if (ref instanceof PsiJavaCodeReferenceElement) {
221 PsiElement last = ((PsiJavaCodeReferenceElement)ref).getReferenceNameElement();
222 if (last instanceof PsiIdentifier && last.getText().equals(changeInfo.getOldName())) {
223 last.replace(changeInfo.getNewNameIdentifier());
228 final PsiMethod caller = RefactoringUtil.getEnclosingMethod(ref);
229 if (toChangeArguments) {
230 final PsiExpressionList list = RefactoringUtil.getArgumentListByMethodReference(ref);
231 boolean toInsertDefaultValue = !(changeInfo instanceof JavaChangeInfoImpl) || !((JavaChangeInfoImpl)changeInfo).propagateParametersMethods.contains(caller);
232 if (toInsertDefaultValue && ref instanceof PsiReferenceExpression) {
233 final PsiExpression qualifierExpression = ((PsiReferenceExpression)ref).getQualifierExpression();
234 if (qualifierExpression instanceof PsiSuperExpression && callerSignatureIsAboutToChangeToo(caller, usages)) {
235 toInsertDefaultValue = false;
239 fixActualArgumentsList(list, changeInfo, toInsertDefaultValue, subsitutor);
242 if (toCatchExceptions) {
243 if (!(ref instanceof PsiReferenceExpression &&
244 ((PsiReferenceExpression)ref).getQualifierExpression() instanceof PsiSuperExpression)) {
245 if (needToCatchExceptions(changeInfo, caller)) {
246 PsiClassType[] newExceptions =
247 callee != null ? getCalleeChangedExceptionInfo(callee) : getPrimaryChangedExceptionInfo(changeInfo);
248 fixExceptions(ref, newExceptions);
254 private static boolean callerSignatureIsAboutToChangeToo(final PsiMethod caller, final UsageInfo[] usages) {
255 for (UsageInfo usage : usages) {
256 if (usage instanceof MethodCallUsageInfo &&
257 MethodSignatureUtil.isSuperMethod(((MethodCallUsageInfo)usage).getReferencedMethod(), caller)) {
264 private static PsiClassType[] getCalleeChangedExceptionInfo(final PsiMethod callee) {
265 return callee.getThrowsList().getReferencedTypes(); //Callee method's throws list is already modified!
268 private static void fixExceptions(PsiElement ref, PsiClassType[] newExceptions) throws IncorrectOperationException {
269 //methods' throws lists are already modified, may use ExceptionUtil.collectUnhandledExceptions
270 newExceptions = filterCheckedExceptions(newExceptions);
272 PsiElement context = PsiTreeUtil.getParentOfType(ref, PsiTryStatement.class, PsiMethod.class);
273 if (context instanceof PsiTryStatement) {
274 PsiTryStatement tryStatement = (PsiTryStatement)context;
275 PsiCodeBlock tryBlock = tryStatement.getTryBlock();
277 //Remove unused catches
278 Collection<PsiClassType> classes = ExceptionUtil.collectUnhandledExceptions(tryBlock, tryBlock);
279 PsiParameter[] catchParameters = tryStatement.getCatchBlockParameters();
280 for (PsiParameter parameter : catchParameters) {
281 final PsiType caughtType = parameter.getType();
283 if (!(caughtType instanceof PsiClassType)) continue;
284 if (ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)caughtType)) continue;
286 if (!isCatchParameterRedundant((PsiClassType)caughtType, classes)) continue;
287 parameter.getParent().delete(); //delete catch section
290 PsiClassType[] exceptionsToAdd = filterUnhandledExceptions(newExceptions, tryBlock);
291 addExceptions(exceptionsToAdd, tryStatement);
293 adjustPossibleEmptyTryStatement(tryStatement);
296 newExceptions = filterUnhandledExceptions(newExceptions, ref);
297 if (newExceptions.length > 0) {
298 //Add new try statement
299 PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(ref.getProject());
300 PsiTryStatement tryStatement = (PsiTryStatement)elementFactory.createStatementFromText("try {} catch (Exception e) {}", null);
301 PsiStatement anchor = PsiTreeUtil.getParentOfType(ref, PsiStatement.class);
302 LOG.assertTrue(anchor != null);
303 tryStatement.getTryBlock().add(anchor);
304 tryStatement = (PsiTryStatement)anchor.getParent().addAfter(tryStatement, anchor);
306 addExceptions(newExceptions, tryStatement);
308 tryStatement.getCatchSections()[0].delete(); //Delete dummy catch section
313 private static PsiClassType[] filterCheckedExceptions(PsiClassType[] exceptions) {
314 List<PsiClassType> result = new ArrayList<PsiClassType>();
315 for (PsiClassType exceptionType : exceptions) {
316 if (!ExceptionUtil.isUncheckedException(exceptionType)) result.add(exceptionType);
318 return result.toArray(new PsiClassType[result.size()]);
321 private static void adjustPossibleEmptyTryStatement(PsiTryStatement tryStatement) throws IncorrectOperationException {
322 PsiCodeBlock tryBlock = tryStatement.getTryBlock();
323 if (tryBlock != null) {
324 if (tryStatement.getCatchSections().length == 0 &&
325 tryStatement.getFinallyBlock() == null) {
326 PsiElement firstBodyElement = tryBlock.getFirstBodyElement();
327 if (firstBodyElement != null) {
328 tryStatement.getParent().addRangeAfter(firstBodyElement, tryBlock.getLastBodyElement(), tryStatement);
330 tryStatement.delete();
335 private static void addExceptions(PsiClassType[] exceptionsToAdd, PsiTryStatement tryStatement) throws IncorrectOperationException {
336 for (PsiClassType type : exceptionsToAdd) {
337 final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(tryStatement.getProject());
338 String name = styleManager.suggestVariableName(VariableKind.PARAMETER, null, null, type).names[0];
339 name = styleManager.suggestUniqueVariableName(name, tryStatement, false);
341 PsiCatchSection catchSection =
342 JavaPsiFacade.getInstance(tryStatement.getProject()).getElementFactory().createCatchSection(type, name, tryStatement);
343 tryStatement.add(catchSection);
347 private static PsiClassType[] filterUnhandledExceptions(PsiClassType[] exceptions, PsiElement place) {
348 List<PsiClassType> result = new ArrayList<PsiClassType>();
349 for (PsiClassType exception : exceptions) {
350 if (!ExceptionUtil.isHandled(exception, place)) result.add(exception);
352 return result.toArray(new PsiClassType[result.size()]);
355 private static boolean isCatchParameterRedundant(PsiClassType catchParamType, Collection<PsiClassType> thrownTypes) {
356 for (PsiType exceptionType : thrownTypes) {
357 if (exceptionType.isConvertibleFrom(catchParamType)) return false;
362 //This methods works equally well for primary usages as well as for propagated callers' usages
363 private static void fixActualArgumentsList(PsiExpressionList list,
364 JavaChangeInfo changeInfo,
365 boolean toInsertDefaultValue, PsiSubstitutor substitutor) throws IncorrectOperationException {
366 final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory();
367 if (changeInfo.isParameterSetOrOrderChanged()) {
368 if (changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).isPropagationEnabled) {
369 final ParameterInfoImpl[] createdParmsInfo = ((JavaChangeInfoImpl)changeInfo).getCreatedParmsInfoWithoutVarargs();
370 for (ParameterInfoImpl info : createdParmsInfo) {
371 PsiExpression newArg;
372 if (toInsertDefaultValue) {
373 newArg = createDefaultValue(changeInfo, factory, info, list);
376 newArg = factory.createExpressionFromText(info.getName(), list);
382 final PsiExpression[] args = list.getExpressions();
383 final int nonVarargCount = getNonVarargCount(changeInfo, args);
384 final int varargCount = args.length - nonVarargCount;
385 PsiExpression[] newVarargInitializers = null;
387 final int newArgsLength;
388 final int newNonVarargCount;
389 final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
390 if (changeInfo.isArrayToVarargs()) {
391 newNonVarargCount = newParms.length - 1;
392 final JavaParameterInfo lastNewParm = newParms[newParms.length - 1];
393 final PsiExpression arrayToConvert = args[lastNewParm.getOldIndex()];
394 if (arrayToConvert instanceof PsiNewExpression) {
395 final PsiNewExpression expression = (PsiNewExpression)arrayToConvert;
396 final PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
397 if (arrayInitializer != null) {
398 newVarargInitializers = arrayInitializer.getInitializers();
401 newArgsLength = newVarargInitializers == null ? newParms.length : newNonVarargCount + newVarargInitializers.length;
403 else if (changeInfo.isRetainsVarargs()) {
404 newNonVarargCount = newParms.length - 1;
405 newArgsLength = newNonVarargCount + varargCount;
407 else if (changeInfo.isObtainsVarags()) {
408 newNonVarargCount = newParms.length - 1;
409 newArgsLength = newNonVarargCount;
412 newNonVarargCount = newParms.length;
413 newArgsLength = newParms.length;
416 String[] oldVarargs = null;
417 if (changeInfo.wasVararg() && !changeInfo.isRetainsVarargs()) {
418 oldVarargs = new String[varargCount];
419 for (int i = nonVarargCount; i < args.length; i++) {
420 oldVarargs[i - nonVarargCount] = args[i].getText();
424 final PsiExpression[] newArgs = new PsiExpression[newArgsLength];
425 for (int i = 0; i < newNonVarargCount; i++) {
426 if (newParms[i].getOldIndex() == nonVarargCount && oldVarargs != null) {
427 PsiType type = newParms[i].createType(changeInfo.getMethod(), list.getManager());
428 if (type instanceof PsiArrayType) {
429 type = substitutor.substitute(type);
430 type = TypeConversionUtil.erasure(type);
431 String typeText = type.getCanonicalText();
432 if (type instanceof PsiEllipsisType) {
433 typeText = typeText.replace("...", "[]");
435 String text = "new " + typeText + "{" + StringUtil.join(oldVarargs, ",") + "}";
436 newArgs[i] = factory.createExpressionFromText(text, changeInfo.getMethod());
440 newArgs[i] = createActualArgument(changeInfo, list, newParms[i], toInsertDefaultValue, args);
442 if (changeInfo.isArrayToVarargs()) {
443 if (newVarargInitializers == null) {
444 newArgs[newNonVarargCount] =
445 createActualArgument(changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args);
448 System.arraycopy(newVarargInitializers, 0, newArgs, newNonVarargCount, newVarargInitializers.length);
452 final int newVarargCount = newArgsLength - newNonVarargCount;
453 LOG.assertTrue(newVarargCount == 0 || newVarargCount == varargCount);
454 System.arraycopy(args, nonVarargCount, newArgs, newNonVarargCount, newVarargCount);
456 ChangeSignatureUtil.synchronizeList(list, Arrays.asList(newArgs), ExpressionList.INSTANCE, changeInfo.toRemoveParm());
461 private static int getNonVarargCount(JavaChangeInfo changeInfo, PsiExpression[] args) {
462 if (!changeInfo.wasVararg()) return args.length;
463 return changeInfo.getOldParameterTypes().length - 1;
468 private static PsiExpression createActualArgument(JavaChangeInfo changeInfo,
469 final PsiExpressionList list,
470 final JavaParameterInfo info,
471 final boolean toInsertDefaultValue,
472 final PsiExpression[] args) throws IncorrectOperationException {
473 final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory();
474 final int index = info.getOldIndex();
479 if (toInsertDefaultValue) {
480 return createDefaultValue(changeInfo, factory, info, list);
483 return factory.createExpressionFromText(info.getName(), list);
489 private static PsiExpression createDefaultValue(JavaChangeInfo changeInfo,
490 final PsiElementFactory factory,
491 final JavaParameterInfo info,
492 final PsiExpressionList list)
493 throws IncorrectOperationException {
494 if (info.isUseAnySingleVariable()) {
495 final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper();
496 final PsiType type = info.getTypeWrapper().getType(changeInfo.getMethod(), list.getManager());
497 final VariablesProcessor processor = new VariablesProcessor(false) {
498 protected boolean check(PsiVariable var, ResolveState state) {
499 if (var instanceof PsiField && !resolveHelper.isAccessible((PsiField)var, list, null)) return false;
500 final PsiType varType = state.get(PsiSubstitutor.KEY).substitute(var.getType());
501 return type.isAssignableFrom(varType);
504 public boolean execute(PsiElement pe, ResolveState state) {
505 super.execute(pe, state);
509 PsiScopesUtil.treeWalkUp(processor, list, null);
510 if (processor.size() == 1) {
511 final PsiVariable result = processor.getResult(0);
512 return factory.createExpressionFromText(result.getName(), list);
515 final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(list, PsiCallExpression.class);
516 return callExpression != null ? info.getValue(callExpression) : factory.createExpressionFromText(info.getDefaultValue(), list);
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) {
535 private static void generateDelegate(JavaChangeInfo changeInfo) throws IncorrectOperationException {
536 final PsiMethod delegate = (PsiMethod)changeInfo.getMethod().copy();
537 final PsiClass targetClass = changeInfo.getMethod().getContainingClass();
538 LOG.assertTrue(!targetClass.isInterface());
539 PsiElementFactory factory = JavaPsiFacade.getElementFactory(targetClass.getProject());
540 ChangeSignatureProcessor.makeEmptyBody(factory, delegate);
541 final PsiCallExpression callExpression = ChangeSignatureProcessor.addDelegatingCallTemplate(delegate, changeInfo.getNewName());
542 addDelegateArguments(changeInfo, factory, callExpression);
543 targetClass.addBefore(delegate, changeInfo.getMethod());
547 private static void addDelegateArguments(JavaChangeInfo changeInfo, PsiElementFactory factory, final PsiCallExpression callExpression) throws IncorrectOperationException {
548 final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
549 final String[] oldParameterNames = changeInfo.getOldParameterNames();
550 for (int i = 0; i < newParms.length; i++) {
551 JavaParameterInfo newParm = newParms[i];
552 final PsiExpression actualArg;
553 if (newParm.getOldIndex() >= 0) {
554 actualArg = factory.createExpressionFromText(oldParameterNames[newParm.getOldIndex()], callExpression);
557 actualArg = changeInfo.getValue(i, callExpression);
559 callExpression.getArgumentList().add(actualArg);
563 private static void processPrimaryMethod(JavaChangeInfo changeInfo, PsiMethod method,
564 PsiMethod baseMethod,
565 boolean isOriginal) throws IncorrectOperationException {
566 PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
568 if (changeInfo.isVisibilityChanged()) {
569 PsiModifierList modifierList = method.getModifierList();
570 final String highestVisibility = isOriginal
571 ? changeInfo.getNewVisibility()
572 : VisibilityUtil.getHighestVisibility(changeInfo.getNewVisibility(),
573 VisibilityUtil.getVisibilityModifier(modifierList));
574 VisibilityUtil.setVisibility(modifierList, highestVisibility);
577 if (changeInfo.isNameChanged()) {
578 String newName = baseMethod == null ? changeInfo.getNewName() :
579 RefactoringUtil.suggestNewOverriderName(method.getName(), baseMethod.getName(), changeInfo.getNewName());
581 if (newName != null && !newName.equals(method.getName())) {
582 final PsiIdentifier nameId = method.getNameIdentifier();
583 assert nameId != null;
584 nameId.replace(JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createIdentifier(newName));
588 final PsiSubstitutor substitutor =
589 baseMethod == null ? PsiSubstitutor.EMPTY : ChangeSignatureProcessor.calculateSubstitutor(method, baseMethod);
591 if (changeInfo.isReturnTypeChanged()) {
592 PsiType newTypeElement = changeInfo.getNewReturnType().getType(changeInfo.getMethod().getParameterList(), method.getManager());
593 final PsiType returnType = substitutor.substitute(newTypeElement);
594 // don't modify return type for non-Java overriders (EJB)
595 if (method.getName().equals(changeInfo.getNewName())) {
596 final PsiTypeElement typeElement = method.getReturnTypeElement();
597 if (typeElement != null) {
598 typeElement.replace(factory.createTypeElement(returnType));
603 PsiParameterList list = method.getParameterList();
604 PsiParameter[] parameters = list.getParameters();
606 final JavaParameterInfo[] parameterInfos = changeInfo.getNewParameters();
607 PsiParameter[] newParms = new PsiParameter[parameterInfos.length -
608 (baseMethod != null ? baseMethod.getParameterList().getParametersCount() -
609 method.getParameterList().getParametersCount() : 0)];
610 final String[] oldParameterNames = changeInfo.getOldParameterNames();
611 final String[] oldParameterTypes = changeInfo.getOldParameterTypes();
612 for (int i = 0; i < newParms.length; i++) {
613 JavaParameterInfo info = parameterInfos[i];
614 int index = info.getOldIndex();
616 PsiParameter parameter = parameters[index];
617 newParms[i] = parameter;
619 String oldName = oldParameterNames[index];
620 if (!oldName.equals(info.getName()) && oldName.equals(parameter.getName())) {
621 PsiIdentifier newIdentifier = factory.createIdentifier(info.getName());
622 parameter.getNameIdentifier().replace(newIdentifier);
625 String oldType = oldParameterTypes[index];
626 if (!oldType.equals(info.getTypeText())) {
627 parameter.normalizeDeclaration();
628 PsiType newType = substitutor.substitute(info.createType(changeInfo.getMethod().getParameterList(), method.getManager()));
630 parameter.getTypeElement().replace(factory.createTypeElement(newType));
634 newParms[i] = createNewParameter(changeInfo, info, substitutor);
639 resolveParameterVsFieldsConflicts(newParms, method, list, changeInfo.toRemoveParm());
640 fixJavadocsForChangedMethod(method, changeInfo, newParms.length);
641 if (changeInfo.isExceptionSetOrOrderChanged()) {
642 final PsiClassType[] newExceptions = getPrimaryChangedExceptionInfo(changeInfo);
643 fixPrimaryThrowsLists(method, newExceptions);
647 private static PsiClassType[] getPrimaryChangedExceptionInfo(JavaChangeInfo changeInfo) throws IncorrectOperationException {
648 final ThrownExceptionInfo[] newExceptionInfos = changeInfo.getNewExceptions();
649 PsiClassType[] newExceptions = new PsiClassType[newExceptionInfos.length];
650 final PsiMethod method = changeInfo.getMethod();
651 for (int i = 0; i < newExceptions.length; i++) {
653 (PsiClassType)newExceptionInfos[i].createType(method, method.getManager()); //context really does not matter here
655 return newExceptions;
659 private static void processCallerMethod(JavaChangeInfo changeInfo, PsiMethod caller,
660 PsiMethod baseMethod,
661 boolean toInsertParams,
662 boolean toInsertThrows) throws IncorrectOperationException {
663 LOG.assertTrue(toInsertParams || toInsertThrows);
664 if (toInsertParams) {
665 List<PsiParameter> newParameters = new ArrayList<PsiParameter>();
666 ContainerUtil.addAll(newParameters, caller.getParameterList().getParameters());
667 final JavaParameterInfo[] primaryNewParms = changeInfo.getNewParameters();
668 PsiSubstitutor substitutor =
669 baseMethod == null ? PsiSubstitutor.EMPTY : ChangeSignatureProcessor.calculateSubstitutor(caller, baseMethod);
670 for (JavaParameterInfo info : primaryNewParms) {
671 if (info.getOldIndex() < 0) newParameters.add(createNewParameter(changeInfo, info, substitutor));
673 PsiParameter[] arrayed = newParameters.toArray(new PsiParameter[newParameters.size()]);
674 boolean[] toRemoveParm = new boolean[arrayed.length];
675 Arrays.fill(toRemoveParm, false);
676 resolveParameterVsFieldsConflicts(arrayed, caller, caller.getParameterList(), toRemoveParm);
679 if (toInsertThrows) {
680 List<PsiJavaCodeReferenceElement> newThrowns = new ArrayList<PsiJavaCodeReferenceElement>();
681 final PsiReferenceList throwsList = caller.getThrowsList();
682 ContainerUtil.addAll(newThrowns, throwsList.getReferenceElements());
683 final ThrownExceptionInfo[] primaryNewExns = changeInfo.getNewExceptions();
684 for (ThrownExceptionInfo thrownExceptionInfo : primaryNewExns) {
685 if (thrownExceptionInfo.getOldIndex() < 0) {
686 final PsiClassType type = (PsiClassType)thrownExceptionInfo.createType(caller, caller.getManager());
687 final PsiJavaCodeReferenceElement ref =
688 JavaPsiFacade.getInstance(caller.getProject()).getElementFactory().createReferenceElementByType(type);
692 PsiJavaCodeReferenceElement[] arrayed = newThrowns.toArray(new PsiJavaCodeReferenceElement[newThrowns.size()]);
693 boolean[] toRemoveParm = new boolean[arrayed.length];
694 Arrays.fill(toRemoveParm, false);
695 ChangeSignatureUtil.synchronizeList(throwsList, Arrays.asList(arrayed), ThrowsList.INSTANCE, toRemoveParm);
699 private static void fixPrimaryThrowsLists(PsiMethod method, PsiClassType[] newExceptions) throws IncorrectOperationException {
700 PsiElementFactory elementFactory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
701 PsiJavaCodeReferenceElement[] refs = new PsiJavaCodeReferenceElement[newExceptions.length];
702 for (int i = 0; i < refs.length; i++) {
703 refs[i] = elementFactory.createReferenceElementByType(newExceptions[i]);
705 PsiReferenceList throwsList = elementFactory.createReferenceList(refs);
707 PsiReferenceList methodThrowsList = (PsiReferenceList)method.getThrowsList().replace(throwsList);
708 methodThrowsList = (PsiReferenceList)JavaCodeStyleManager.getInstance(method.getProject()).shortenClassReferences(methodThrowsList);
709 method.getManager().getCodeStyleManager().reformatRange(method, method.getParameterList().getTextRange().getEndOffset(),
710 methodThrowsList.getTextRange().getEndOffset());
713 private static void fixJavadocsForChangedMethod(PsiMethod method, JavaChangeInfo changeInfo, int newParamsLength) throws IncorrectOperationException {
714 final PsiParameter[] parameters = method.getParameterList().getParameters();
715 final JavaParameterInfo[] newParms = changeInfo.getNewParameters();
716 LOG.assertTrue(parameters.length <= newParamsLength);
717 final Set<PsiParameter> newParameters = new HashSet<PsiParameter>();
718 final String[] oldParameterNames = changeInfo.getOldParameterNames();
719 for (int i = 0; i < newParamsLength; i++) {
720 JavaParameterInfo newParm = newParms[i];
721 if (newParm.getOldIndex() < 0 ||
722 !newParm.getName().equals(oldParameterNames[newParm.getOldIndex()])) {
723 newParameters.add(parameters[i]);
726 RefactoringUtil.fixJavadocsForParams(method, newParameters);
729 private static PsiParameter createNewParameter(JavaChangeInfo changeInfo, JavaParameterInfo newParm,
730 PsiSubstitutor substitutor) throws IncorrectOperationException {
731 final PsiParameterList list = changeInfo.getMethod().getParameterList();
732 final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory();
733 final PsiType type = substitutor.substitute(newParm.createType(list, list.getManager()));
734 return factory.createParameter(newParm.getName(), type);
737 private static void resolveParameterVsFieldsConflicts(final PsiParameter[] newParms,
738 final PsiMethod method,
739 final PsiParameterList list,
740 boolean[] toRemoveParm) throws IncorrectOperationException {
741 List<FieldConflictsResolver> conflictResolvers = new ArrayList<FieldConflictsResolver>();
742 for (PsiParameter parameter : newParms) {
743 conflictResolvers.add(new FieldConflictsResolver(parameter.getName(), method.getBody()));
745 ChangeSignatureUtil.synchronizeList(list, Arrays.asList(newParms), ParameterList.INSTANCE, toRemoveParm);
746 JavaCodeStyleManager.getInstance(list.getProject()).shortenClassReferences(list);
747 for (FieldConflictsResolver fieldConflictsResolver : conflictResolvers) {
748 fieldConflictsResolver.fix();
752 private static boolean needToCatchExceptions(JavaChangeInfo changeInfo, PsiMethod caller) {
753 return changeInfo.isExceptionSetOrOrderChanged() &&
754 !(changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl)changeInfo).propagateExceptionsMethods.contains(caller));
757 private static class ParameterList implements ChangeSignatureUtil.ChildrenGenerator<PsiParameterList, PsiParameter> {
758 public static final ParameterList INSTANCE = new ParameterList();
760 public List<PsiParameter> getChildren(PsiParameterList psiParameterList) {
761 return Arrays.asList(psiParameterList.getParameters());
765 private static class ThrowsList implements ChangeSignatureUtil.ChildrenGenerator<PsiReferenceList, PsiJavaCodeReferenceElement> {
766 public static final ThrowsList INSTANCE = new ThrowsList();
768 public List<PsiJavaCodeReferenceElement> getChildren(PsiReferenceList throwsList) {
769 return Arrays.asList(throwsList.getReferenceElements());
773 private static class ConflictSearcher {
774 private final JavaChangeInfo myChangeInfo;
776 private ConflictSearcher(JavaChangeInfo changeInfo) {
777 this.myChangeInfo = changeInfo;
780 public MultiMap<PsiElement, String> findConflicts(Ref<UsageInfo[]> refUsages) {
781 MultiMap<PsiElement, String> conflictDescriptions = new MultiMap<PsiElement, String>();
782 addMethodConflicts(conflictDescriptions);
783 Set<UsageInfo> usagesSet = new HashSet<UsageInfo>(Arrays.asList(refUsages.get()));
784 RenameUtil.removeConflictUsages(usagesSet);
785 if (myChangeInfo.isVisibilityChanged()) {
787 addInaccessibilityDescriptions(usagesSet, conflictDescriptions);
789 catch (IncorrectOperationException e) {
794 return conflictDescriptions;
797 private boolean needToChangeCalls() {
798 return myChangeInfo.isNameChanged() || myChangeInfo.isParameterSetOrOrderChanged() || myChangeInfo.isExceptionSetOrOrderChanged();
802 private void addInaccessibilityDescriptions(Set<UsageInfo> usages, MultiMap<PsiElement, String> conflictDescriptions)
803 throws IncorrectOperationException {
804 PsiMethod method = myChangeInfo.getMethod();
805 PsiModifierList modifierList = (PsiModifierList)method.getModifierList().copy();
806 VisibilityUtil.setVisibility(modifierList, myChangeInfo.getNewVisibility());
808 for (Iterator<UsageInfo> iterator = usages.iterator(); iterator.hasNext();) {
809 UsageInfo usageInfo = iterator.next();
810 PsiElement element = usageInfo.getElement();
811 if (element != null && StdLanguages.JAVA.equals(element.getLanguage())) {
812 if (element instanceof PsiReferenceExpression) {
813 PsiClass accessObjectClass = null;
814 PsiExpression qualifier = ((PsiReferenceExpression)element).getQualifierExpression();
815 if (qualifier != null) {
816 accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
819 if (!JavaPsiFacade.getInstance(element.getProject()).getResolveHelper()
820 .isAccessible(method, modifierList, element, accessObjectClass, null)) {
822 RefactoringBundle.message("0.with.1.visibility.is.not.accessible.from.2",
823 RefactoringUIUtil.getDescription(method, true),
824 myChangeInfo.getNewVisibility(),
825 RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(element), true));
826 conflictDescriptions.putValue(method, message);
827 if (!needToChangeCalls()) {
837 private void addMethodConflicts(MultiMap<PsiElement, String> conflicts) {
838 String newMethodName = myChangeInfo.getNewName();
839 if (!(myChangeInfo instanceof JavaChangeInfo)) {
844 final PsiMethod method = myChangeInfo.getMethod();
845 if (!StdLanguages.JAVA.equals(method.getLanguage())) return;
846 PsiManager manager = PsiManager.getInstance(method.getProject());
847 PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
848 final CanonicalTypes.Type returnType = myChangeInfo.getNewReturnType();
849 if (returnType != null) {
850 prototype = factory.createMethod(newMethodName, returnType.getType(method, manager));
853 prototype = factory.createConstructor();
854 prototype.setName(newMethodName);
856 JavaParameterInfo[] parameters = myChangeInfo.getNewParameters();
859 for (JavaParameterInfo info : parameters) {
860 PsiType parameterType = info.createType(method, manager);
861 if (parameterType == null) {
863 JavaPsiFacade.getElementFactory(method.getProject()).createTypeFromText(CommonClassNames.JAVA_LANG_OBJECT, method);
865 PsiParameter param = factory.createParameter(info.getName(), parameterType);
866 prototype.getParameterList().add(param);
869 ConflictsUtil.checkMethodConflicts(method.getContainingClass(), method, prototype, conflicts);
871 catch (IncorrectOperationException e) {
877 private static class ExpressionList implements ChangeSignatureUtil.ChildrenGenerator<PsiExpressionList, PsiExpression> {
878 public static final ExpressionList INSTANCE = new ExpressionList();
880 public List<PsiExpression> getChildren(PsiExpressionList psiExpressionList) {
881 return Arrays.asList(psiExpressionList.getExpressions());